Added more utility methods to the stream serializer class.

It now supports the default Minecraft string encodign (UTF-16).
This commit is contained in:
Kristian S. Stangeland 2013-07-17 20:07:02 +02:00
parent 9f0d3a5054
commit b0cec61d66
3 changed files with 113 additions and 9 deletions

View File

@ -10,6 +10,7 @@ import java.util.PriorityQueue;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
@ -27,6 +28,9 @@ public class NetworkMarker {
private final ConnectionSide side; private final ConnectionSide side;
// Cache serializer too
private StreamSerializer serializer;
/** /**
* Construct a new network marker. * Construct a new network marker.
* <p> * <p>
@ -50,6 +54,16 @@ public class NetworkMarker {
return side; return side;
} }
/**
* Retrieve a utility class for serializing and deserializing Minecraft objects.
* @return Serialization utility class.
*/
public StreamSerializer getSerializer() {
if (serializer == null)
serializer = new StreamSerializer();
return serializer;
}
/** /**
* Retrieve the serialized packet data (excluding the header) from the network input stream. * Retrieve the serialized packet data (excluding the header) from the network input stream.
* <p> * <p>

View File

@ -24,8 +24,11 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
*/ */
public class StreamSerializer { public class StreamSerializer {
// Cached methods // Cached methods
private static Method readItemMethod; private static Method READ_ITEM_METHOD;
private static Method writeItemMethod; private static Method WRITE_ITEM_METHOD;
private static Method READ_STRING_METHOD;
private static Method WRITE_STRING_METHOD;
/** /**
* Read or deserialize an item stack from an underlying input stream. * Read or deserialize an item stack from an underlying input stream.
@ -40,8 +43,8 @@ public class StreamSerializer {
public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException { public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException {
if (input == null) if (input == null)
throw new IllegalArgumentException("Input stream cannot be NULL."); throw new IllegalArgumentException("Input stream cannot be NULL.");
if (readItemMethod == null) { if (READ_ITEM_METHOD == null) {
readItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( READ_ITEM_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder(). FuzzyMethodContract.newBuilder().
parameterCount(1). parameterCount(1).
parameterDerivedOf(DataInput.class). parameterDerivedOf(DataInput.class).
@ -49,7 +52,7 @@ public class StreamSerializer {
build()); build());
} }
try { try {
Object nmsItem = readItemMethod.invoke(null, input); Object nmsItem = READ_ITEM_METHOD.invoke(null, input);
// Convert back to a Bukkit item stack // Convert back to a Bukkit item stack
return MinecraftReflection.getBukkitItemStack(nmsItem); return MinecraftReflection.getBukkitItemStack(nmsItem);
@ -59,6 +62,41 @@ public class StreamSerializer {
} }
} }
/**
* Deserialize a string using the standard Minecraft UTF-16 encoding.
* <p>
* 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
* @throws IOException
*/
public String deserializeString(@Nonnull DataInputStream input, int maximumLength) throws IOException {
if (input == null)
throw new IllegalArgumentException("Input stream cannot be NULL.");
if (maximumLength > 32767)
throw new IllegalArgumentException("Maximum lenght cannot exceed 32767 characters.");
if (maximumLength < 0)
throw new IllegalArgumentException("Maximum lenght cannot be negative.");
if (READ_STRING_METHOD == null) {
READ_STRING_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(DataInput.class, 0).
parameterExactType(int.class, 1).
returnTypeExact(String.class).
build());
}
try {
// Convert back to a Bukkit item stack
return (String) READ_STRING_METHOD.invoke(null, input, maximumLength);
} catch (Exception e) {
throw new IOException("Cannot read Minecraft string.", e);
}
}
/** /**
* Deserialize an item stack from a base-64 encoded string. * Deserialize an item stack from a base-64 encoded string.
* @param input - base-64 encoded string. * @param input - base-64 encoded string.
@ -68,7 +106,6 @@ public class StreamSerializer {
public ItemStack deserializeItemStack(@Nonnull String input) throws IOException { public ItemStack deserializeItemStack(@Nonnull String input) throws IOException {
if (input == null) if (input == null)
throw new IllegalArgumentException("Input text cannot be NULL."); throw new IllegalArgumentException("Input text cannot be NULL.");
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input)); ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input));
return deserializeItemStack(new DataInputStream(inputStream)); return deserializeItemStack(new DataInputStream(inputStream));
@ -93,20 +130,53 @@ public class StreamSerializer {
// Get the NMS version of the ItemStack // Get the NMS version of the ItemStack
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack); Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
if (writeItemMethod == null) if (WRITE_ITEM_METHOD == null)
writeItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( WRITE_ITEM_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder(). FuzzyMethodContract.newBuilder().
parameterCount(2). parameterCount(2).
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0). parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
parameterDerivedOf(DataOutput.class, 1). parameterDerivedOf(DataOutput.class, 1).
build()); build());
try { try {
writeItemMethod.invoke(null, nmsItem, output); WRITE_ITEM_METHOD.invoke(null, nmsItem, output);
} catch (Exception e) { } catch (Exception e) {
throw new IOException("Cannot write item stack " + stack, e); throw new IOException("Cannot write item stack " + stack, e);
} }
} }
/**
* Deserialize a string using the standard Minecraft UTF-16 encoding.
* <p>
* 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
* @throws IOException
*/
public void serializeString(@Nonnull DataOutputStream output, String text) throws IOException {
if (output == null)
throw new IllegalArgumentException("output stream cannot be NULL.");
if (text == null)
throw new IllegalArgumentException("text cannot be NULL.");
if (WRITE_STRING_METHOD == null) {
WRITE_STRING_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterExactType(String.class, 0).
parameterDerivedOf(DataOutput.class, 1).
returnTypeVoid().
build());
}
try {
// Convert back to a Bukkit item stack
WRITE_STRING_METHOD.invoke(null, text, output);
} catch (Exception e) {
throw new IOException("Cannot read Minecraft string.", e);
}
}
/** /**
* Serialize an item stack as a base-64 encoded string. * Serialize an item stack as a base-64 encoded string.
* <p> * <p>

View File

@ -2,6 +2,10 @@ package com.comphenix.protocol.utility;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import org.bukkit.Material; import org.bukkit.Material;
@ -33,4 +37,20 @@ public class StreamSerializerTest {
assertEquals(before.getType(), after.getType()); assertEquals(before.getType(), after.getType());
assertEquals(before.getAmount(), after.getAmount()); assertEquals(before.getAmount(), after.getAmount());
} }
@Test
public void testStrings() throws IOException {
StreamSerializer serializer = new StreamSerializer();
String initial = "Hello - this is a ÆØÅ test.";
// Buffer
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
serializer.serializeString(new DataOutputStream(buffer), initial);
DataInputStream input = new DataInputStream(
new ByteArrayInputStream(buffer.toByteArray()));
String deserialized = serializer.deserializeString(input, 50);
assertEquals(initial, deserialized);
}
} }