Merge branch 'master' into gh-pages
Dieser Commit ist enthalten in:
Commit
9778ed6fc5
1
.gitignore
vendored
Normale Datei
1
.gitignore
vendored
Normale Datei
@ -0,0 +1 @@
|
||||
dependency-reduced-pom.xml
|
@ -1,218 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<name>ProtocolLib</name>
|
||||
<version>1.9.0</version>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
<url>http://dev.bukkit.org/server-mods/protocollib/</url>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>aadnk</id>
|
||||
<name>Kristian S. Stangeland</name>
|
||||
<email>kr_stang@hotmail.com</email>
|
||||
<url>http://comphenix.net/</url>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
<role>maintainer</role>
|
||||
</roles>
|
||||
<timezone>1</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
|
||||
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/aadnk/ProtocolLib.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:aadnk/ProtocolLib.git</developerConnection>
|
||||
<url>https://github.com/aadnk/ProtocolLib</url>
|
||||
</scm>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<excludes>
|
||||
<exclude>**/*.java</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.sf</pattern>
|
||||
<shadedPattern>com.comphenix.net.sf</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.bukkit:craftbukkit</exclude>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>bukkit-rep</id>
|
||||
<url>http://repo.bukkit.org/content/groups/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.4.6-R0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>powermock-module-junit4-common</artifactId>
|
||||
<groupId>org.powermock</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>powermock-api-support</artifactId>
|
||||
<groupId>org.powermock</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<configuration>
|
||||
<threshold>High</threshold>
|
||||
<effort>Default</effort>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>comphenix-releases</id>
|
||||
<name>Comphenix Maven Releases</name>
|
||||
<url>http://repo.comphenix.net/content/repositories/releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>comphenix-snapshots</id>
|
||||
<name>Comphenix Maven Snapshots</name>
|
||||
<url>http://repo.comphenix.net/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
<properties>
|
||||
<powermock.version>1.5</powermock.version>
|
||||
<project.build.sourceEncoding>cp1252</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<version>2.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||
|
||||
|
@ -51,10 +51,12 @@ 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;
|
||||
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -235,6 +237,14 @@ public class PacketContainer implements Serializable {
|
||||
public StructureModifier<byte[]> 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.
|
||||
@ -355,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<NbtBase<?>> 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.
|
||||
* <p>
|
||||
|
@ -70,85 +70,73 @@ import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.
|
||||
* </p>
|
||||
* <p>
|
||||
* Public methods provided by this class:
|
||||
* </p>
|
||||
* <p> The metrics class obtains data about a plugin and submits statistics about it to the metrics backend. </p> <p>
|
||||
* Public methods provided by this class: </p>
|
||||
* <code>
|
||||
* Graph createGraph(String name); <br/>
|
||||
* void addCustomData(Metrics.Plotter plotter); <br/>
|
||||
* void addCustomData(BukkitMetrics.Plotter plotter); <br/>
|
||||
* void start(); <br/>
|
||||
* </code>
|
||||
*/
|
||||
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<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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:</p>
|
||||
* <p>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:</p>
|
||||
* <code>
|
||||
* 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 <b>unmodifiable</b> 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<Plotter> 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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<Object> previous = new HashSet<Object>();
|
||||
|
||||
|
@ -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;
|
||||
|
||||
@ -150,41 +148,59 @@ public class BackgroundCompiler {
|
||||
if (executor == null || executor.isShutdown())
|
||||
return;
|
||||
|
||||
try {
|
||||
executor.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
StructureModifier<TKey> 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<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
StructureModifier<TKey> 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 +225,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();
|
||||
}
|
||||
}
|
||||
|
@ -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<StructureKey, Class> compiledCache = new HashMap<StructureKey, Class>();
|
||||
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
|
||||
|
||||
// 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 <TField> boolean lookupClassLoader(StructureModifier<TField> 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.
|
||||
* <p>
|
||||
@ -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 <TField> Class<?> generateClass(StructureModifier<TField> 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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -0,0 +1,103 @@
|
||||
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 org.bukkit.inventory.ItemStack;
|
||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input));
|
||||
|
||||
return deserializeItemStack(new DataInputStream(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write or serialize an item stack to the given output stream.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an item stack as a base-64 encoded string.
|
||||
* @param stack - the item stack to serialize.
|
||||
* @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 {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataOutput = new DataOutputStream(outputStream);
|
||||
|
||||
serializeItemStack(dataOutput, stack);
|
||||
|
||||
// Serialize that array
|
||||
return Base64Coder.encodeLines(outputStream.toByteArray());
|
||||
}
|
||||
}
|
@ -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.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
|
||||
/**
|
||||
* 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<NbtBase<?>> getNbtConverter() {
|
||||
return getIgnoreNull(new EquivalentConverter<NbtBase<?>>() {
|
||||
@Override
|
||||
public Object getGeneric(Class<?> genericType, NbtBase<?> specific) {
|
||||
return NbtFactory.fromBase(specific).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtBase<?> getSpecific(Object generic) {
|
||||
return NbtFactory.fromNMS(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<NbtBase<?>> getSpecificType() {
|
||||
// Damn you Java AGAIN
|
||||
Class<?> dummy = NbtBase.class;
|
||||
return (Class<NbtBase<?>>) dummy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a converter for NMS entities and Bukkit entities.
|
||||
* @param world - the current world.
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* Represents an object that transform elements of type VInner to type VOuter and back again.
|
||||
*
|
||||
* @author Kristian
|
||||
*
|
||||
* @param <VInner> - the first type.
|
||||
* @param <VOuter> - the second type.
|
||||
*/
|
||||
abstract class AbstractConverted<VInner, VOuter> {
|
||||
/**
|
||||
* 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<VInner, VOuter> getOuterConverter() {
|
||||
return new Function<VInner, VOuter>() {
|
||||
@Override
|
||||
public VOuter apply(@Nullable VInner param) {
|
||||
return toOuter(param);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
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 <VInner> - type of the element in the inner invisible collection.
|
||||
* @param <VOuter> - type of the elements publically accessible in the outer collection.
|
||||
*/
|
||||
abstract class ConvertedCollection<VInner, VOuter> extends AbstractConverted<VInner, VOuter> implements Collection<VOuter> {
|
||||
// Inner collection
|
||||
private Collection<VInner> inner;
|
||||
|
||||
public ConvertedCollection(Collection<VInner> inner) {
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(VOuter e) {
|
||||
return inner.add(toInner(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends VOuter> 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<VOuter> 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<VInner> 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> 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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;
|
||||
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 <VInner> - type of the items in the inner invisible list.
|
||||
* @param <VOuter> - type of the items publically accessible in the outer list.
|
||||
*/
|
||||
abstract class ConvertedList<VInner, VOuter> extends ConvertedCollection<VInner, VOuter> implements List<VOuter> {
|
||||
private List<VInner> inner;
|
||||
|
||||
public ConvertedList(List<VInner> 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<? extends VOuter> 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<VOuter> listIterator() {
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<VOuter> listIterator(int index) {
|
||||
final ListIterator<VInner> innerIterator = inner.listIterator(index);
|
||||
|
||||
return new ListIterator<VOuter>() {
|
||||
@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<VOuter> subList(int fromIndex, int toIndex) {
|
||||
return new ConvertedList<VInner, VOuter>(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<VOuter, VInner> getInnerCollection(Collection c) {
|
||||
return new ConvertedCollection<VOuter, VInner>(c) {
|
||||
@Override
|
||||
protected VOuter toInner(VInner outer) {
|
||||
return ConvertedList.this.toOuter(outer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VInner toOuter(VOuter inner) {
|
||||
return ConvertedList.this.toInner(inner);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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;
|
||||
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 <VInner> - type of the value in the entries in the inner invisible map.
|
||||
* @param <VOuter> - type of the value in the entries publically accessible in the outer map.
|
||||
*/
|
||||
abstract class ConvertedMap<Key, VInner, VOuter> extends AbstractConverted<VInner, VOuter> implements Map<Key, VOuter> {
|
||||
// Inner map
|
||||
private Map<Key, VInner> inner;
|
||||
|
||||
public ConvertedMap(Map<Key, VInner> 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<Entry<Key, VOuter>> entrySet() {
|
||||
return new ConvertedSet<Entry<Key,VInner>, Entry<Key,VOuter>>(inner.entrySet()) {
|
||||
@Override
|
||||
protected Entry<Key, VInner> toInner(final Entry<Key, VOuter> outer) {
|
||||
return new Entry<Key, VInner>() {
|
||||
@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<Key, VOuter> toOuter(final Entry<Key, VInner> inner) {
|
||||
return new Entry<Key, VOuter>() {
|
||||
@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<Key> keySet() {
|
||||
return inner.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VOuter put(Key key, VOuter value) {
|
||||
return toOuter(inner.put(key, toInner(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends Key, ? extends VOuter> m) {
|
||||
for (Entry<? extends Key, ? extends VOuter> 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<VOuter> values() {
|
||||
return new ConvertedCollection<VInner, VOuter>(inner.values()) {
|
||||
@Override
|
||||
protected VOuter toOuter(VInner inner) {
|
||||
return ConvertedMap.this.toOuter(inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VInner toInner(VOuter outer) {
|
||||
return ConvertedMap.this.toInner(outer);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a set that wraps another set by transforming the items going in and out.
|
||||
*
|
||||
* @author Kristian
|
||||
*
|
||||
* @param <VInner> - type of the element in the inner invisible set.
|
||||
* @param <VOuter> - type of the elements publically accessible in the outer set.
|
||||
*/
|
||||
abstract class ConvertedSet<VInner, VOuter> extends ConvertedCollection<VInner, VOuter> implements Set<VOuter> {
|
||||
public ConvertedSet(Collection<VInner> inner) {
|
||||
super(inner);
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtType;
|
||||
|
||||
/**
|
||||
* Represents a generic container for an NBT element.
|
||||
* <p>
|
||||
* Use {@link NbtFactory} to load or create an instance.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TType> - type of the value that is stored.
|
||||
*/
|
||||
public interface NbtBase<TType> {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public abstract NbtType getType();
|
||||
|
||||
/**
|
||||
* Retrieve the name of this NBT tag.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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<TType> deepClone();
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* Use {@link NbtFactory} to load or create an instance.
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
public interface NbtCompound extends NbtBase<Map<String, NbtBase<?>>>, Iterable<NbtBase<?>> {
|
||||
/**
|
||||
* 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 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 abstract Set<String> getKeys();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public abstract <T> NbtBase<T> 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 abstract NbtBase<?> getValueOrDefault(String key, NbtType type);
|
||||
|
||||
/**
|
||||
* Set a entry based on its name.
|
||||
* @param entry - entry with a name and value.
|
||||
* @return This compound, for chaining.
|
||||
*/
|
||||
public abstract <T> NbtCompound put(NbtBase<T> 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 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 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 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.
|
||||
* @param key - the key of the entry.
|
||||
* @return The byte value of the entry.
|
||||
* @throws IllegalArgumentException If the key doesn't exist.
|
||||
*/
|
||||
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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.
|
||||
*/
|
||||
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 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 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.
|
||||
*/
|
||||
public abstract <T> NbtList<T> 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.
|
||||
*/
|
||||
public abstract <T> NbtList<T> getListOrDefault(String key);
|
||||
|
||||
/**
|
||||
* Associate a NBT list with the given key.
|
||||
* @param list - the list value.
|
||||
* @return This current compound, for chaining.
|
||||
*/
|
||||
public abstract <T> NbtCompound put(NbtList<T> 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 abstract <T> NbtCompound put(String key, Collection<? extends NbtBase<T>> list);
|
||||
|
||||
/**
|
||||
* Retrieve an iterator view of the NBT tags stored in this compound.
|
||||
* @return The tags stored in this compound.
|
||||
*/
|
||||
public abstract Iterator<NbtBase<?>> iterator();
|
||||
}
|
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* 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.Method;
|
||||
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.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NbtFactory {
|
||||
// Used to create the underlying tag
|
||||
private static Method methodCreateTag;
|
||||
|
||||
// Item stack trickery
|
||||
private static StructureModifier<Object> itemStackModifier;
|
||||
|
||||
/**
|
||||
* 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(NbtBase<?> tag) {
|
||||
if (tag instanceof NbtCompound)
|
||||
return (NbtCompound) tag;
|
||||
else if (tag != null)
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot cast a " + tag.getClass() + "( " + tag.getType() + ") to TAG_COMPUND.");
|
||||
else
|
||||
throw new IllegalArgumentException("Tag cannot be NULL.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(NbtBase<?> tag) {
|
||||
if (tag instanceof NbtList)
|
||||
return (NbtList<?>) tag;
|
||||
else if (tag != null)
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot cast a " + tag.getClass() + "( " + tag.getType() + ") to TAG_LIST.");
|
||||
else
|
||||
throw new IllegalArgumentException("Tag cannot be NULL.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a NBT wrapper from a NBT base.
|
||||
* <p>
|
||||
* 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 <T> NbtWrapper<T> fromBase(NbtBase<T> base) {
|
||||
if (base instanceof NbtWrapper) {
|
||||
return (NbtWrapper<T>) base;
|
||||
} else {
|
||||
if (base.getType() == NbtType.TAG_COMPOUND) {
|
||||
// Load into a NBT-backed wrapper
|
||||
WrappedCompound copy = WrappedCompound.fromName(base.getName());
|
||||
T value = base.getValue();
|
||||
|
||||
copy.setValue((Map<String, NbtBase<?>>) value);
|
||||
return (NbtWrapper<T>) copy;
|
||||
|
||||
} else if (base.getType() == NbtType.TAG_LIST) {
|
||||
// As above
|
||||
NbtList<T> copy = WrappedList.fromName(base.getName());
|
||||
|
||||
copy.setValue((List<NbtBase<T>>) base.getValue());
|
||||
return (NbtWrapper<T>) copy;
|
||||
|
||||
} else {
|
||||
// Copy directly
|
||||
NbtWrapper<T> copy = ofWrapper(base.getType(), base.getName());
|
||||
|
||||
copy.setValue(base.getValue());
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<?> fromItemTag(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<Object>(nmsStack.getClass(), Object.class, false);
|
||||
}
|
||||
|
||||
// Use the first and best NBT tag
|
||||
StructureModifier<NbtBase<?>> modifier = itemStackModifier.
|
||||
withTarget(nmsStack).
|
||||
withType(MinecraftReflection.getNBTBaseClass(), BukkitConverters.getNbtConverter());
|
||||
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 fromBase(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a NBT wrapper.
|
||||
* @param handle - the underlying net.minecraft.server object to wrap.
|
||||
* @return A NBT wrapper.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <T> NbtWrapper<T> fromNMS(Object handle) {
|
||||
WrappedElement<T> partial = new WrappedElement<T>(handle);
|
||||
|
||||
// See if this is actually a compound tag
|
||||
if (partial.getType() == NbtType.TAG_COMPOUND)
|
||||
return (NbtWrapper<T>) new WrappedCompound(handle);
|
||||
else if (partial.getType() == NbtType.TAG_LIST)
|
||||
return new WrappedList(handle);
|
||||
else
|
||||
return partial;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> of(String name, String value) {
|
||||
return ofWrapper(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<Byte> of(String name, byte value) {
|
||||
return ofWrapper(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<Short> of(String name, short value) {
|
||||
return ofWrapper(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<Integer> of(String name, int value) {
|
||||
return ofWrapper(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<Long> of(String name, long value) {
|
||||
return ofWrapper(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<Float> of(String name, float value) {
|
||||
return ofWrapper(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<Double> of(String name, double value) {
|
||||
return ofWrapper(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<byte[]> of(String name, byte[] value) {
|
||||
return ofWrapper(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<int[]> of(String name, int[] value) {
|
||||
return ofWrapper(NbtType.TAG_INT_ARRAY, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static NbtCompound ofCompound(String name, Collection<? extends NbtBase<?>> list) {
|
||||
return WrappedCompound.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 WrappedCompound.fromName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <T> NbtList<T> ofList(String name, T... elements) {
|
||||
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 <T> NbtList<T> ofList(String name, Collection<? extends T> elements) {
|
||||
return WrappedList.fromList(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 <T> NbtWrapper<T> ofWrapper(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<T>) new WrappedCompound(handle);
|
||||
else if (type == NbtType.TAG_LIST)
|
||||
return (NbtWrapper<T>) new WrappedList(handle);
|
||||
else
|
||||
return new WrappedElement<T>(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 <T> NbtWrapper<T> ofWrapper(NbtType type, String name, T value) {
|
||||
NbtWrapper<T> created = ofWrapper(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 <T> NbtWrapper<T> ofWrapper(Class<?> type, String name, T value) {
|
||||
return ofWrapper(NbtType.getTypeFromClass(type), name, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a list of NBT tags of the same type without names.
|
||||
* <p>
|
||||
* Use {@link NbtFactory} to load or create an instance.
|
||||
* <p>
|
||||
* 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
|
||||
*
|
||||
* @param <TType> - the value type of each NBT tag.
|
||||
*/
|
||||
public interface NbtList<TType> extends NbtBase<List<NbtBase<TType>>>, Iterable<TType> {
|
||||
/**
|
||||
* The name of every NBT tag in a list.
|
||||
*/
|
||||
public static String EMPTY_NAME = "";
|
||||
|
||||
/**
|
||||
* Get the type of each element.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
public abstract void add(NbtBase<TType> element);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 abstract Collection<NbtBase<TType>> asCollection();
|
||||
|
||||
/**
|
||||
* Iterate over all the elements in this list.
|
||||
*/
|
||||
public abstract Iterator<TType> iterator();
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
/**
|
||||
* 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<Class<?>, NbtType> classLookup;
|
||||
|
||||
static {
|
||||
NbtType[] values = values();
|
||||
lookup = new NbtType[values.length];
|
||||
classLookup = new HashMap<Class<?>, NbtType>();
|
||||
|
||||
// Initialize lookup tables
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Indicates that this NBT wraps an underlying net.minecraft.server instance.
|
||||
* <p>
|
||||
* Use {@link NbtFactory} to load or create instances.
|
||||
*
|
||||
* @author Kristian
|
||||
*
|
||||
* @param <TType> - type of the value that is stored.
|
||||
*/
|
||||
public interface NbtWrapper<TType> extends NbtBase<TType> {
|
||||
/**
|
||||
* 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);
|
||||
}
|
@ -0,0 +1,630 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
|
||||
/**
|
||||
* A concrete implementation of an NbtCompound that wraps an underlying NMS Compound.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class WrappedCompound implements NbtWrapper<Map<String, NbtBase<?>>>, Iterable<NbtBase<?>>, NbtCompound {
|
||||
// A list container
|
||||
private WrappedElement<Map<String, Object>> container;
|
||||
|
||||
// Saved wrapper map
|
||||
private ConvertedMap<String, Object, NbtBase<?>> 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.<Map<String, NbtBase<?>>>ofWrapper(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<? extends NbtBase<?>> 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.
|
||||
*/
|
||||
public WrappedCompound(Object handle) {
|
||||
this.container = new WrappedElement<Map<String,Object>>(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() {
|
||||
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<String> getKeys() {
|
||||
return getValue().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, NbtBase<?>> getValue() {
|
||||
// Return a wrapper map
|
||||
if (savedMap == null) {
|
||||
savedMap = new ConvertedMap<String, Object, NbtBase<?>>(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<String, NbtBase<?>> newValue) {
|
||||
// Write all the entries
|
||||
for (Map.Entry<String, NbtBase<?>> 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 <T> NbtBase<T> getValue(String key) {
|
||||
return (NbtBase<T>) 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.ofWrapper(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 <T> NbtBase<T> getValueExact(String key) {
|
||||
NbtBase<T> 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<Map<String, 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 <T> NbtCompound put(NbtBase<T> 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 <T> NbtList<T> 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 <T> NbtList<T> getListOrDefault(String key) {
|
||||
return (NbtList<T>) 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 <T> NbtCompound put(NbtList<T> 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.
|
||||
* @param list - the list of NBT elements.
|
||||
* @return This current compound, for chaining.
|
||||
*/
|
||||
@Override
|
||||
public <T> NbtCompound put(String key, Collection<? extends NbtBase<T>> list) {
|
||||
return put(WrappedList.fromList(key, list));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput destination) {
|
||||
NbtBinarySerializer.DEFAULT.serialize(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<NbtBase<?>> 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();
|
||||
}
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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.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.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Represents a wrapped NBT tag element, composite or not.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TType> - type of the value field.
|
||||
*/
|
||||
class WrappedElement<TType> implements NbtWrapper<TType> {
|
||||
// Structure modifier for the base class
|
||||
private static volatile StructureModifier<Object> baseModifier;
|
||||
|
||||
// For retrieving the current type ID
|
||||
private static volatile Method methodGetTypeID;
|
||||
|
||||
// For handling cloning
|
||||
private static volatile 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.
|
||||
*/
|
||||
public WrappedElement(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<String> getBaseModifier() {
|
||||
if (baseModifier == null) {
|
||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||
|
||||
// This will be the same for all classes, so we'll share modifier
|
||||
baseModifier = new StructureModifier<Object>(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<TType> 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<Object> getCurrentBaseModifier() {
|
||||
int index = getType().ordinal();
|
||||
StructureModifier<Object> modifier = (StructureModifier<Object>) modifiers[index];
|
||||
|
||||
// Double checked locking
|
||||
if (modifier == null) {
|
||||
synchronized (this) {
|
||||
if (modifiers[index] == null) {
|
||||
modifiers[index] = new StructureModifier<Object>(handle.getClass(), MinecraftReflection.getNBTBaseClass(), false);
|
||||
}
|
||||
modifier = (StructureModifier<Object>) modifiers[index];
|
||||
}
|
||||
}
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(NbtVisitor visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the sub element type of the underlying NMS NBT list.
|
||||
* @return The NBT sub type.
|
||||
*/
|
||||
public NbtType getSubType() {
|
||||
int subID = getCurrentBaseModifier().<Byte>withType(byte.class).withTarget(handle).read(0);
|
||||
return NbtType.getTypeFromID(subID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().<Byte>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) {
|
||||
// No need to cache this object
|
||||
NbtBinarySerializer.DEFAULT.serialize(this, destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtBase<TType> deepClone() {
|
||||
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();
|
||||
}
|
||||
}
|
@ -0,0 +1,407 @@
|
||||
/*
|
||||
* 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.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
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 <TType> - the type of the value in each NBT sub element.
|
||||
*/
|
||||
class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, Iterable<TType>, NbtList<TType> {
|
||||
// A list container
|
||||
private WrappedElement<List<Object>> container;
|
||||
|
||||
// Saved wrapper list
|
||||
private ConvertedList<Object, NbtBase<TType>> 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> NbtList<T> fromName(String name) {
|
||||
return (NbtList<T>) NbtFactory.<List<NbtBase<T>>>ofWrapper(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.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <T> NbtList<T> fromArray(String name, T... elements) {
|
||||
NbtList<T> result = fromName(name);
|
||||
|
||||
for (T element : elements) {
|
||||
if (element == null)
|
||||
throw new IllegalArgumentException("An NBT list cannot contain a null element!");
|
||||
|
||||
if (element instanceof NbtBase)
|
||||
result.add((NbtBase) element);
|
||||
else
|
||||
result.add(NbtFactory.ofWrapper(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.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static <T> NbtList<T> fromList(String name, Collection<? extends T> elements) {
|
||||
NbtList<T> result = fromName(name);
|
||||
|
||||
for (T element : elements) {
|
||||
if (element == null)
|
||||
throw new IllegalArgumentException("An NBT list cannot contain a null 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<List<Object>>(handle);
|
||||
this.elementType = container.getSubType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(NbtVisitor visitor) {
|
||||
// Enter this node?
|
||||
if (visitor.visitEnter(this)) {
|
||||
for (NbtBase<TType> node : getValue()) {
|
||||
if (!node.accept(visitor))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return visitor.visitLeave(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return container.getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtType getType() {
|
||||
return NbtType.TAG_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtType getElementType() {
|
||||
return elementType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElementType(NbtType type) {
|
||||
this.elementType = type;
|
||||
container.setSubType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return container.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
container.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NbtBase<TType>> getValue() {
|
||||
if (savedList == null) {
|
||||
savedList = new ConvertedList<Object, NbtBase<TType>>(container.getValue()) {
|
||||
// Check and see if the element is valid
|
||||
private void verifyElement(NbtBase<TType> 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 (getElementType() != NbtType.TAG_END) {
|
||||
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<TType> e) {
|
||||
verifyElement(e);
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, NbtBase<TType> element) {
|
||||
verifyElement(element);
|
||||
super.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends NbtBase<TType>> c) {
|
||||
boolean result = false;
|
||||
|
||||
for (NbtBase<TType> element : c) {
|
||||
add(element);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object toInner(NbtBase<TType> outer) {
|
||||
if (outer == null)
|
||||
return null;
|
||||
return NbtFactory.fromBase(outer).getHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NbtBase<TType> 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<List<NbtBase<TType>>> deepClone() {
|
||||
return (NbtBase) container.deepClone();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addClosest(Object value) {
|
||||
if (getElementType() == NbtType.TAG_END)
|
||||
throw new IllegalStateException("This list has not been typed yet.");
|
||||
|
||||
if (value instanceof Number) {
|
||||
Number number = (Number) value;
|
||||
|
||||
// Convert the number
|
||||
switch (getElementType()) {
|
||||
case TAG_BYTE: add(number.byteValue()); break;
|
||||
case TAG_SHORT: add(number.shortValue()); break;
|
||||
case TAG_INT: add(number.intValue()); break;
|
||||
case TAG_LONG: add(number.longValue()); break;
|
||||
case TAG_FLOAT: add(number.floatValue()); break;
|
||||
case TAG_DOUBLE: add(number.doubleValue()); break;
|
||||
case TAG_STRING: add(number.toString()); break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot convert " + value + " to " + getType());
|
||||
}
|
||||
|
||||
} else if (value instanceof NbtBase) {
|
||||
// Add the element itself
|
||||
add((NbtBase<TType>) value);
|
||||
|
||||
} else {
|
||||
// Just add it
|
||||
add((NbtBase<TType>) NbtFactory.ofWrapper(getElementType(), EMPTY_NAME, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(NbtBase<TType> element) {
|
||||
getValue().add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(String value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(byte value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(short value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(int value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(long value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(double value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(byte[] value) {
|
||||
add((NbtBase<TType>) NbtFactory.of(EMPTY_NAME, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void add(int[] value) {
|
||||
add((NbtBase<TType>) 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<NbtBase<TType>> asCollection() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(List<NbtBase<TType>> newValue) {
|
||||
NbtBase<TType> lastElement = null;
|
||||
List<Object> list = container.getValue();
|
||||
list.clear();
|
||||
|
||||
// Set each underlying element
|
||||
for (NbtBase<TType> 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) {
|
||||
NbtBinarySerializer.DEFAULT.serialize(container, destination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof WrappedList) {
|
||||
@SuppressWarnings("unchecked")
|
||||
WrappedList<TType> other = (WrappedList<TType>) obj;
|
||||
return container.equals(other.container);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return container.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<TType> iterator() {
|
||||
return Iterables.transform(getValue(), new Function<NbtBase<TType>, TType>() {
|
||||
@Override
|
||||
public TType apply(@Nullable NbtBase<TType> 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.comphenix.protocol.wrappers.nbt.io;
|
||||
|
||||
import java.io.DataInput;
|
||||
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.utility.MinecraftReflection;
|
||||
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.NbtWrapper;
|
||||
|
||||
public class NbtBinarySerializer {
|
||||
// Used to read and write NBT
|
||||
private static Method methodWrite;
|
||||
private static Method methodLoad;
|
||||
|
||||
/**
|
||||
* Retrieve a default instance of the NBT binary serializer.
|
||||
*/
|
||||
public static final NbtBinarySerializer DEFAULT = new NbtBinarySerializer();
|
||||
|
||||
/**
|
||||
* Write the content of a wrapped NBT tag to a stream.
|
||||
* @param value - the NBT tag to write.
|
||||
* @param destination - the destination stream.
|
||||
*/
|
||||
public <TType> void serialize(NbtBase<TType> 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 <TType> NbtWrapper<TType> 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 <T> NbtList<T> deserializeList(DataInput source) {
|
||||
return (NbtList<T>) (NbtBase) deserialize(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,347 @@
|
||||
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;
|
||||
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.
|
||||
* <p>
|
||||
* 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 <TType> void serialize(NbtBase<TType> value, final ConfigurationSection destination) {
|
||||
value.accept(new NbtVisitor() {
|
||||
private ConfigurationSection current = destination;
|
||||
|
||||
// The current list we're working on
|
||||
private List<Object> currentList;
|
||||
|
||||
// Store the index of a configuration section that works like a list
|
||||
private Map<ConfigurationSection, Integer> 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, fromNodeValue(node));
|
||||
|
||||
} else {
|
||||
currentList.add(fromNodeValue(node));
|
||||
}
|
||||
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 <TType> NbtWrapper<TType> deserialize(ConfigurationSection root, String nodeName) {
|
||||
return (NbtWrapper<TType>) 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 <T> NbtList<T> deserializeList(YamlConfiguration root, String nodeName) {
|
||||
return (NbtList<T>) 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<Object> list = NbtFactory.ofList(decoded[0]);
|
||||
ConfigurationSection section = (ConfigurationSection) node;
|
||||
List<String> sorted = sortSet(section.getKeys(false));
|
||||
|
||||
// Read everything in order
|
||||
for (String key : sorted) {
|
||||
NbtBase<Object> base = (NbtBase<Object>) 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<Object> list = NbtFactory.ofList(decoded[0]);
|
||||
list.setElementType(type);
|
||||
|
||||
for (Object value : (List<Object>) node) {
|
||||
list.addClosest(toNodeValue(value, type));
|
||||
}
|
||||
|
||||
// Add the list
|
||||
return (NbtWrapper<?>) list;
|
||||
|
||||
} else {
|
||||
// Normal node
|
||||
return NbtFactory.ofWrapper(type, decoded[0], toNodeValue(node, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> sortSet(Set<String> unsorted) {
|
||||
// Convert to integers
|
||||
List<String> sorted = new ArrayList<String>(unsorted);
|
||||
|
||||
Collections.sort(sorted, new Comparator<String>() {
|
||||
@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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
* <p>
|
||||
* 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)
|
||||
return new String[] { nodeName.substring(0, delimiter), nodeName.substring(delimiter + 1) };
|
||||
else
|
||||
return new String[] { nodeName };
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
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 org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||
|
||||
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-64 encoded string and back.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NbtTextSerializer {
|
||||
/**
|
||||
* A default instance of this serializer.
|
||||
*/
|
||||
public static final NbtTextSerializer DEFAULT = new NbtTextSerializer();
|
||||
|
||||
private NbtBinarySerializer binarySerializer;
|
||||
|
||||
public NbtTextSerializer() {
|
||||
this(new NbtBinarySerializer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a serializer with a custom binary serializer.
|
||||
* @param binary - binary serializer.
|
||||
*/
|
||||
public NbtTextSerializer(NbtBinarySerializer binary) {
|
||||
this.binarySerializer = binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the binary serializer that is used.
|
||||
* @return The binary serializer.
|
||||
*/
|
||||
public NbtBinarySerializer getBinarySerializer() {
|
||||
return binarySerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a NBT tag to a base-64 encoded string.
|
||||
* @param value - the NBT tag to serialize.
|
||||
* @return The NBT tag in base-64 form.
|
||||
*/
|
||||
public <TType> String serialize(NbtBase<TType> value) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataOutput = new DataOutputStream(outputStream);
|
||||
|
||||
binarySerializer.serialize(value, dataOutput);
|
||||
|
||||
// Serialize that array
|
||||
return Base64Coder.encodeLines(outputStream.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <TType> NbtWrapper<TType> deserialize(String input) throws IOException {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input));
|
||||
|
||||
return binarySerializer.deserialize(new DataInputStream(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@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-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.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> NbtList<T> deserializeList(String input) throws IOException {
|
||||
return (NbtList<T>) (NbtBase) deserialize(input);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
name: ProtocolLib
|
||||
version: 1.9.0
|
||||
version: 2.0.0
|
||||
description: Provides read/write access to the Minecraft protocol.
|
||||
author: Comphenix
|
||||
website: http://www.comphenix.net/ProtocolLib
|
||||
|
@ -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.*;
|
||||
|
@ -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.*;
|
||||
|
@ -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.*;
|
||||
@ -32,6 +49,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 +252,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);
|
||||
|
@ -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.*;
|
||||
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
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<Integer> test = new NbtCustomTag<Integer>("hello", 12);
|
||||
|
||||
WrappedCompound map = WrappedCompound.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 <TValue> - the value of the tag.
|
||||
*/
|
||||
public static class NbtCustomTag<TValue> implements NbtBase<TValue> {
|
||||
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<TValue> deepClone() {
|
||||
return new NbtCustomTag<TValue>(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(NbtVisitor visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
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;
|
||||
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
|
||||
|
||||
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() {
|
||||
WrappedCompound compound = WrappedCompound.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 = NbtBinarySerializer.DEFAULT.deserializeCompound(input);
|
||||
|
||||
assertEquals(compound.getString("name"), cloned.getString("name"));
|
||||
assertEquals(compound.getInteger("age"), cloned.getInteger("age"));
|
||||
assertEquals(compound.getList("nicknames"), cloned.getList("nicknames"));
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
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("values", new int[] { 1, 2, 3});
|
||||
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);
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren