Added the ability to serialize and deserialize NbtCompounds.

This commit is contained in:
Kristian S. Stangeland 2013-07-17 20:31:25 +02:00
parent b0cec61d66
commit aa9d84c639
3 changed files with 98 additions and 2 deletions

View File

@ -8,7 +8,6 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.annotation.Nonnull;
import org.bukkit.inventory.ItemStack;
@ -16,6 +15,8 @@ import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
/**
* Utility methods for reading and writing Minecraft objects to streams.
@ -27,6 +28,9 @@ public class StreamSerializer {
private static Method READ_ITEM_METHOD;
private static Method WRITE_ITEM_METHOD;
private static Method READ_NBT_METHOD;
private static Method WRITE_NBT_METHOD;
private static Method READ_STRING_METHOD;
private static Method WRITE_STRING_METHOD;
@ -61,6 +65,31 @@ public class StreamSerializer {
throw new IOException("Cannot read item stack.", e);
}
}
/**
* Read or deserialize an NBT compound from a input stream.
* @param input - the target input stream.
* @return The resulting compound, or NULL.
* @throws IOException If the operation failed due to reflection or corrupt data.
*/
public NbtCompound deserializeCompound(@Nonnull DataInputStream input) throws IOException {
if (input == null)
throw new IllegalArgumentException("Input stream cannot be NULL.");
if (READ_NBT_METHOD == null) {
READ_NBT_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(1).
parameterDerivedOf(DataInput.class).
returnDerivedOf(MinecraftReflection.getNBTBaseClass()).
build());
}
try {
// Convert back to an NBT Compound
return NbtFactory.fromNMSCompound(READ_NBT_METHOD.invoke(null, input));
} catch (Exception e) {
throw new IOException("Cannot read item stack.", e);
}
}
/**
* Deserialize a string using the standard Minecraft UTF-16 encoding.
@ -68,7 +97,7 @@ public class StreamSerializer {
* Note that strings cannot exceed 32767 characters, regardless if maximum lenght.
* @param input - the input stream.
* @param maximumLength - the maximum lenght of the string.
* @return
* @return The deserialized string.
* @throws IOException
*/
public String deserializeString(@Nonnull DataInputStream input, int maximumLength) throws IOException {
@ -144,6 +173,40 @@ public class StreamSerializer {
}
}
/**
* Write or serialize a NBT compound to the given output stream.
* <p>
* Note: An NBT compound can be written to a stream even if it's NULL.
*
* @param output - the target output stream.
* @param stack - the NBT compound to be serialized, or NULL to represent nothing.
* @throws IOException If the operation fails due to reflection problems.
*/
public void serializeCompound(@Nonnull DataOutputStream output, NbtCompound compound) throws IOException {
if (output == null)
throw new IllegalArgumentException("Output stream cannot be NULL.");
// Get the NMS version of the compound
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
if (WRITE_NBT_METHOD == null) {
WRITE_NBT_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(MinecraftReflection.getNBTBaseClass(), 0).
parameterDerivedOf(DataOutput.class, 1).
returnTypeVoid().
build());
WRITE_NBT_METHOD.setAccessible(true);
}
try {
WRITE_NBT_METHOD.invoke(null, handle, output);
} catch (Exception e) {
throw new IOException("Cannot write compound " + compound, e);
}
}
/**
* Deserialize a string using the standard Minecraft UTF-16 encoding.
* <p>

View File

@ -22,6 +22,8 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.FieldAccessException;
@ -163,6 +165,17 @@ public class NbtFactory {
return partial;
}
/**
* Retrieve the NBT compound from a given NMS handle.
* @param handle - the underlying net.minecraft.server object to wrap.
* @return A NBT compound wrapper
*/
public static NbtCompound fromNMSCompound(@Nonnull Object handle) {
if (handle == null)
throw new IllegalArgumentException("handle cannot be NULL.");
return (NbtCompound) NbtFactory.<Map<String, NbtBase<?>>>fromNMS(handle);
}
/**
* Constructs a NBT tag of type string.
* @param name - name of the tag.

View File

@ -17,6 +17,8 @@ import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PrepareForTest(CraftItemFactory.class)
@ -53,4 +55,22 @@ public class StreamSerializerTest {
assertEquals(initial, deserialized);
}
@Test
public void testCompound() throws IOException {
StreamSerializer serializer = new StreamSerializer();
NbtCompound initial = NbtFactory.ofCompound("tag");
initial.put("name", "Ole");
initial.put("age", 20);
// Buffer
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
serializer.serializeCompound(new DataOutputStream(buffer), initial);
DataInputStream input = new DataInputStream(
new ByteArrayInputStream(buffer.toByteArray()));
NbtCompound deserialized = serializer.deserializeCompound(input);
assertEquals(initial, deserialized);
}
}