mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-28 05:35:28 +01:00
Fix the StreamSerializer
String-based methods will still work, but deserializing from a data input stream has been deprecated since it depends on hacky code Fixes #31, Fixes #125
This commit is contained in:
parent
4bb917e596
commit
7dff86cb48
@ -61,10 +61,6 @@ public class Netty {
|
|||||||
return getCompat().createPacketBuffer();
|
return getCompat().createPacketBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WrappedByteBuf allocateUnpooled() {
|
|
||||||
return getCompat().allocateUnpooled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class<?> getGenericFutureListenerArray() {
|
public static Class<?> getGenericFutureListenerArray() {
|
||||||
return getCompat().getGenericFutureListenerArray();
|
return getCompat().getGenericFutureListenerArray();
|
||||||
}
|
}
|
||||||
@ -92,4 +88,12 @@ public class Netty {
|
|||||||
public static WrappedByteBuf packetWriter(DataOutputStream output) {
|
public static WrappedByteBuf packetWriter(DataOutputStream output) {
|
||||||
return getCompat().packetWriter(output);
|
return getCompat().packetWriter(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static WrappedByteBuf copiedBuffer(byte[] array) {
|
||||||
|
return getCompat().copiedBuffer(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WrappedByteBuf buffer() {
|
||||||
|
return getCompat().buffer();
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,11 +30,14 @@ import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
|
|||||||
* @author dmulloy2
|
* @author dmulloy2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: Sort out packet readers/writers
|
||||||
public interface NettyCompat {
|
public interface NettyCompat {
|
||||||
|
|
||||||
WrappedByteBuf createPacketBuffer();
|
WrappedByteBuf createPacketBuffer();
|
||||||
|
|
||||||
WrappedByteBuf allocateUnpooled();
|
WrappedByteBuf copiedBuffer(byte[] array);
|
||||||
|
|
||||||
|
WrappedByteBuf buffer();
|
||||||
|
|
||||||
Class<?> getGenericFutureListenerArray();
|
Class<?> getGenericFutureListenerArray();
|
||||||
|
|
||||||
|
@ -41,4 +41,6 @@ public interface WrappedByteBuf {
|
|||||||
void writeByte(int i);
|
void writeByte(int i);
|
||||||
|
|
||||||
void writeBytes(byte[] bytes);
|
void writeBytes(byte[] bytes);
|
||||||
|
|
||||||
|
byte[] array();
|
||||||
}
|
}
|
@ -55,11 +55,6 @@ public class IndependentNetty implements NettyCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public WrappedByteBuf allocateUnpooled() {
|
|
||||||
return new NettyByteBuf(UnpooledByteBufAllocator.DEFAULT.buffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getGenericFutureListenerArray() {
|
public Class<?> getGenericFutureListenerArray() {
|
||||||
return GenericFutureListener[].class;
|
return GenericFutureListener[].class;
|
||||||
@ -97,4 +92,14 @@ public class IndependentNetty implements NettyCompat {
|
|||||||
public WrappedByteBuf packetWriter(DataOutputStream output) {
|
public WrappedByteBuf packetWriter(DataOutputStream output) {
|
||||||
return new NettyByteBuf(NettyByteBufAdapter.packetWriter(output));
|
return new NettyByteBuf(NettyByteBufAdapter.packetWriter(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WrappedByteBuf copiedBuffer(byte[] array) {
|
||||||
|
return new NettyByteBuf(Unpooled.copiedBuffer(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WrappedByteBuf buffer() {
|
||||||
|
return new NettyByteBuf(Unpooled.buffer());
|
||||||
|
}
|
||||||
}
|
}
|
@ -75,4 +75,9 @@ public class NettyByteBuf implements WrappedByteBuf {
|
|||||||
public void writeBytes(byte[] bytes) {
|
public void writeBytes(byte[] bytes) {
|
||||||
handle.get().writeBytes(bytes);
|
handle.get().writeBytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] array() {
|
||||||
|
return handle.get().array();
|
||||||
|
}
|
||||||
}
|
}
|
@ -174,7 +174,7 @@ public class MinecraftMethods {
|
|||||||
// Create our proxy object
|
// Create our proxy object
|
||||||
Object javaProxy = enhancer.create(
|
Object javaProxy = enhancer.create(
|
||||||
new Class<?>[] { MinecraftReflection.getByteBufClass() },
|
new Class<?>[] { MinecraftReflection.getByteBufClass() },
|
||||||
new Object[] { Netty.allocateUnpooled().getHandle() }
|
new Object[] { Netty.buffer().getHandle() }
|
||||||
);
|
);
|
||||||
|
|
||||||
Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle();
|
Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle();
|
||||||
|
@ -10,6 +10,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.Validate;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
|
||||||
|
|
||||||
@ -50,6 +51,22 @@ public class StreamSerializer {
|
|||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a variable integer to an output stream.
|
||||||
|
* @param destination - the destination.
|
||||||
|
* @param value - the value to write.
|
||||||
|
* @throws IOException The destination stream threw an exception.
|
||||||
|
*/
|
||||||
|
public void serializeVarInt(@Nonnull DataOutputStream destination, int value) throws IOException {
|
||||||
|
Preconditions.checkNotNull(destination, "source cannot be NULL");
|
||||||
|
|
||||||
|
while ((value & 0xFFFFFF80) != 0) {
|
||||||
|
destination.writeByte(value & 0x7F | 0x80);
|
||||||
|
value >>>= 7;
|
||||||
|
}
|
||||||
|
destination.writeByte(value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a variable integer from an input stream.
|
* Read a variable integer from an input stream.
|
||||||
* @param source - the source.
|
* @param source - the source.
|
||||||
@ -73,66 +90,49 @@ public class StreamSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a variable integer to an output stream.
|
* Write or serialize a NBT compound to the given output stream.
|
||||||
* @param destination - the destination.
|
|
||||||
* @param value - the value to write.
|
|
||||||
* @throws IOException The destination stream threw an exception.
|
|
||||||
*/
|
|
||||||
public void serializeVarInt(@Nonnull DataOutputStream destination, int value) throws IOException {
|
|
||||||
Preconditions.checkNotNull(destination, "source cannot be NULL");
|
|
||||||
|
|
||||||
while ((value & 0xFFFFFF80) != 0) {
|
|
||||||
destination.writeByte(value & 0x7F | 0x80);
|
|
||||||
value >>>= 7;
|
|
||||||
}
|
|
||||||
destination.writeByte(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read or deserialize an item stack from an underlying input stream.
|
|
||||||
* <p>
|
* <p>
|
||||||
* To supply a byte array, wrap it in a {@link java.io.ByteArrayInputStream ByteArrayInputStream}
|
* Note: An NBT compound can be written to a stream even if it's NULL.
|
||||||
* and {@link java.io.DataInputStream DataInputStream}.
|
|
||||||
*
|
*
|
||||||
* @param input - the target input stream.
|
* @param output - the target output stream.
|
||||||
* @return The resulting item stack, or NULL if the serialized item stack was NULL.
|
* @param compound - the NBT compound to be serialized, or NULL to represent nothing.
|
||||||
* @throws IOException If the operation failed due to reflection or corrupt data.
|
* @throws IOException If the operation fails due to reflection problems.
|
||||||
*/
|
*/
|
||||||
public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException {
|
public void serializeCompound(@Nonnull DataOutputStream output, NbtCompound compound) throws IOException {
|
||||||
if (input == null)
|
if (output == null)
|
||||||
throw new IllegalArgumentException("Input stream cannot be NULL.");
|
throw new IllegalArgumentException("Output stream cannot be NULL.");
|
||||||
Object nmsItem = null;
|
|
||||||
|
// Get the NMS version of the compound
|
||||||
|
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
|
||||||
|
|
||||||
if (MinecraftReflection.isUsingNetty()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
if (READ_ITEM_METHOD == null) {
|
if (WRITE_NBT_METHOD == null) {
|
||||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||||
getMethodByParameters("readItemStack", /* i */
|
getMethodByParameters("writeNbtCompound", /* a */
|
||||||
MinecraftReflection.getItemStackClass(), new Class<?>[0])
|
MinecraftReflection.getNBTCompoundClass())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
nmsItem = READ_ITEM_METHOD.invoke(Netty.packetReader(input).getHandle());
|
WrappedByteBuf buf = Netty.packetWriter(output);
|
||||||
|
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
|
||||||
|
|
||||||
|
WRITE_NBT_METHOD.invoke(buf.getHandle(), handle);
|
||||||
} else {
|
} else {
|
||||||
if (READ_ITEM_METHOD == null) {
|
if (WRITE_NBT_METHOD == null) {
|
||||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
|
||||||
FuzzyMethodContract.newBuilder().
|
FuzzyMethodContract.newBuilder().
|
||||||
parameterCount(1).
|
parameterCount(2).
|
||||||
parameterDerivedOf(DataInput.class).
|
parameterDerivedOf(MinecraftReflection.getNBTBaseClass(), 0).
|
||||||
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
parameterDerivedOf(DataOutput.class, 1).
|
||||||
|
returnTypeVoid().
|
||||||
build())
|
build())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
nmsItem = READ_ITEM_METHOD.invoke(null, input);
|
WRITE_NBT_METHOD.invoke(null, handle, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert back to a Bukkit item stack
|
|
||||||
if (nmsItem != null)
|
|
||||||
return MinecraftReflection.getBukkitItemStack(nmsItem);
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,6 +183,47 @@ public class StreamSerializer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a string using the standard Minecraft UTF-16 encoding.
|
||||||
|
* <p>
|
||||||
|
* Note that strings cannot exceed 32767 characters, regardless if maximum lenght.
|
||||||
|
* @param output - the output stream.
|
||||||
|
* @param text - the string to serialize.
|
||||||
|
* @throws IOException If the data in the string cannot be written.
|
||||||
|
*/
|
||||||
|
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 (MinecraftReflection.isUsingNetty()) {
|
||||||
|
if (WRITE_STRING_METHOD == null) {
|
||||||
|
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||||
|
getMethodByParameters("writeString", /* a */
|
||||||
|
String.class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_STRING_METHOD.invoke(Netty.packetWriter(output).getHandle(), text);
|
||||||
|
} else {
|
||||||
|
if (WRITE_STRING_METHOD == null) {
|
||||||
|
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(2).
|
||||||
|
parameterExactType(String.class, 0).
|
||||||
|
parameterDerivedOf(DataOutput.class, 1).
|
||||||
|
returnTypeVoid().
|
||||||
|
build())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_STRING_METHOD.invoke(null, text, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize a string using the standard Minecraft UTF-16 encoding.
|
* Deserialize a string using the standard Minecraft UTF-16 encoding.
|
||||||
* <p>
|
* <p>
|
||||||
@ -227,18 +268,98 @@ public class StreamSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an item stack as a base-64 encoded string.
|
||||||
|
* <p>
|
||||||
|
* Note: An ItemStack can be written to the serialized text even if it's NULL.
|
||||||
|
*
|
||||||
|
* @param stack - the item stack to serialize, or NULL to represent air/nothing.
|
||||||
|
* @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 {
|
||||||
|
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||||
|
byte[] bytes = null;
|
||||||
|
|
||||||
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
WrappedByteBuf buf = Netty.buffer();
|
||||||
|
Object serializer = MinecraftReflection.getPacketDataSerializer(buf.getHandle());
|
||||||
|
|
||||||
|
if (WRITE_ITEM_METHOD == null) {
|
||||||
|
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||||
|
getMethodByParameters("writeStack", // a()
|
||||||
|
MinecraftReflection.getItemStackClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||||
|
|
||||||
|
bytes = buf.array();
|
||||||
|
} else {
|
||||||
|
if (WRITE_ITEM_METHOD == null) {
|
||||||
|
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(2).
|
||||||
|
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
|
||||||
|
parameterDerivedOf(DataOutput.class, 1).
|
||||||
|
build())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dataOutput = new DataOutputStream(outputStream);
|
||||||
|
|
||||||
|
WRITE_ITEM_METHOD.invoke(null, nmsItem, dataOutput);
|
||||||
|
|
||||||
|
bytes = outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64Coder.encodeLines(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
* @return A deserialized item stack, or NULL if the serialized ItemStack was also NULL.
|
* @return A deserialized item stack, or NULL if the serialized ItemStack was also NULL.
|
||||||
* @throws IOException If the operation failed due to reflection or corrupt data.
|
* @throws IOException If the operation failed due to reflection or corrupt data.
|
||||||
*/
|
*/
|
||||||
public ItemStack deserializeItemStack(@Nonnull String input) throws IOException {
|
public ItemStack deserializeItemStack(String input) throws IOException {
|
||||||
if (input == null)
|
Validate.notNull(input, "input cannot be null!");
|
||||||
throw new IllegalArgumentException("Input text cannot be NULL.");
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input));
|
|
||||||
|
|
||||||
return deserializeItemStack(new DataInputStream(inputStream));
|
Object nmsItem = null;
|
||||||
|
byte[] bytes = Base64Coder.decodeLines(input);
|
||||||
|
|
||||||
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
WrappedByteBuf buf = Netty.copiedBuffer(bytes);
|
||||||
|
Object serializer = MinecraftReflection.getPacketDataSerializer(buf.getHandle());
|
||||||
|
|
||||||
|
if (READ_ITEM_METHOD == null) {
|
||||||
|
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||||
|
getMethodByParameters("readItemStack", // i(ItemStack)
|
||||||
|
MinecraftReflection.getItemStackClass(), new Class<?>[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||||
|
} else {
|
||||||
|
if (READ_ITEM_METHOD == null) {
|
||||||
|
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
|
FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(1).
|
||||||
|
parameterDerivedOf(DataInput.class).
|
||||||
|
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
||||||
|
build())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
|
||||||
|
DataInputStream inputStream = new DataInputStream(byteStream);
|
||||||
|
|
||||||
|
nmsItem = READ_ITEM_METHOD.invoke(null, inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nmsItem != null ? MinecraftReflection.getBukkitItemStack(nmsItem) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,9 +374,8 @@ public class StreamSerializer {
|
|||||||
* @param stack - the item stack that will be written, or NULL to represent air/nothing.
|
* @param stack - the item stack that will be written, or NULL to represent air/nothing.
|
||||||
* @throws IOException If the operation fails due to reflection problems.
|
* @throws IOException If the operation fails due to reflection problems.
|
||||||
*/
|
*/
|
||||||
public void serializeItemStack(@Nonnull DataOutputStream output, ItemStack stack) throws IOException {
|
public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException {
|
||||||
if (output == null)
|
Validate.notNull("output cannot be null!");
|
||||||
throw new IllegalArgumentException("Output stream cannot be NULL.");
|
|
||||||
|
|
||||||
// Get the NMS version of the ItemStack
|
// Get the NMS version of the ItemStack
|
||||||
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||||
@ -269,7 +389,12 @@ public class StreamSerializer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE_ITEM_METHOD.invoke(Netty.packetWriter(output).getHandle(), nmsItem);
|
WrappedByteBuf buf = Netty.buffer();
|
||||||
|
Object serializer = MinecraftReflection.getPacketDataSerializer(buf.getHandle());
|
||||||
|
|
||||||
|
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||||
|
|
||||||
|
output.write(buf.array());
|
||||||
} else {
|
} else {
|
||||||
if (WRITE_ITEM_METHOD == null)
|
if (WRITE_ITEM_METHOD == null)
|
||||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
@ -286,108 +411,56 @@ public class StreamSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write or serialize a NBT compound to the given output stream.
|
* Read or deserialize an item stack from an underlying input stream.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: An NBT compound can be written to a stream even if it's NULL.
|
* To supply a byte array, wrap it in a {@link java.io.ByteArrayInputStream ByteArrayInputStream}
|
||||||
|
* and {@link java.io.DataInputStream DataInputStream}.
|
||||||
*
|
*
|
||||||
* @param output - the target output stream.
|
* @param input - the target input stream.
|
||||||
* @param compound - the NBT compound to be serialized, or NULL to represent nothing.
|
* @return The resulting item stack, or NULL if the serialized item stack was NULL.
|
||||||
* @throws IOException If the operation fails due to reflection problems.
|
* @throws IOException If the operation failed due to reflection or corrupt data.
|
||||||
|
* @deprecated This is a pretty hacky solution for backwards compatibility. See {@link #deserializeItemStack(DataInputStream)}
|
||||||
*/
|
*/
|
||||||
public void serializeCompound(@Nonnull DataOutputStream output, NbtCompound compound) throws IOException {
|
@Deprecated
|
||||||
if (output == null)
|
public ItemStack deserializeItemStack(DataInputStream input) throws IOException {
|
||||||
throw new IllegalArgumentException("Output stream cannot be NULL.");
|
Validate.notNull(input, "input cannot be null!");
|
||||||
|
Object nmsItem = null;
|
||||||
// Get the NMS version of the compound
|
|
||||||
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
|
|
||||||
|
|
||||||
if (MinecraftReflection.isUsingNetty()) {
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
if (WRITE_NBT_METHOD == null) {
|
if (READ_ITEM_METHOD == null) {
|
||||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
||||||
getMethodByParameters("writeNbtCompound", /* a */
|
getMethodByParameters("readItemStack", /* i */
|
||||||
MinecraftReflection.getNBTCompoundClass())
|
MinecraftReflection.getItemStackClass(), new Class<?>[0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedByteBuf buf = Netty.packetWriter(output);
|
byte[] bytes = new byte[8192];
|
||||||
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
|
input.read(bytes);
|
||||||
|
|
||||||
WRITE_NBT_METHOD.invoke(buf.getHandle(), handle);
|
WrappedByteBuf buf = Netty.copiedBuffer(bytes);
|
||||||
|
Object serializer = MinecraftReflection.getPacketDataSerializer(buf.getHandle());
|
||||||
|
|
||||||
|
nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||||
} else {
|
} else {
|
||||||
if (WRITE_NBT_METHOD == null) {
|
if (READ_ITEM_METHOD == null) {
|
||||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
|
READ_ITEM_METHOD = Accessors.getMethodAccessor(
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
|
|
||||||
FuzzyMethodContract.newBuilder().
|
|
||||||
parameterCount(2).
|
|
||||||
parameterDerivedOf(MinecraftReflection.getNBTBaseClass(), 0).
|
|
||||||
parameterDerivedOf(DataOutput.class, 1).
|
|
||||||
returnTypeVoid().
|
|
||||||
build())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE_NBT_METHOD.invoke(null, handle, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a string using the standard Minecraft UTF-16 encoding.
|
|
||||||
* <p>
|
|
||||||
* Note that strings cannot exceed 32767 characters, regardless if maximum lenght.
|
|
||||||
* @param output - the output stream.
|
|
||||||
* @param text - the string to serialize.
|
|
||||||
* @throws IOException If the data in the string cannot be written.
|
|
||||||
*/
|
|
||||||
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 (MinecraftReflection.isUsingNetty()) {
|
|
||||||
if (WRITE_STRING_METHOD == null) {
|
|
||||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
|
|
||||||
getMethodByParameters("writeString", /* a */
|
|
||||||
String.class)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE_STRING_METHOD.invoke(Netty.packetWriter(output).getHandle(), text);
|
|
||||||
} else {
|
|
||||||
if (WRITE_STRING_METHOD == null) {
|
|
||||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
|
|
||||||
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
|
||||||
FuzzyMethodContract.newBuilder().
|
FuzzyMethodContract.newBuilder().
|
||||||
parameterCount(2).
|
parameterCount(1).
|
||||||
parameterExactType(String.class, 0).
|
parameterDerivedOf(DataInput.class).
|
||||||
parameterDerivedOf(DataOutput.class, 1).
|
returnDerivedOf(MinecraftReflection.getItemStackClass()).
|
||||||
returnTypeVoid().
|
|
||||||
build())
|
build())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE_STRING_METHOD.invoke(null, text, output);
|
nmsItem = READ_ITEM_METHOD.invoke(null, input);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Convert back to a Bukkit item stack
|
||||||
* Serialize an item stack as a base-64 encoded string.
|
if (nmsItem != null)
|
||||||
* <p>
|
return MinecraftReflection.getBukkitItemStack(nmsItem);
|
||||||
* Note: An ItemStack can be written to the serialized text even if it's NULL.
|
else
|
||||||
*
|
return null;
|
||||||
* @param stack - the item stack to serialize, or NULL to represent air/nothing.
|
|
||||||
* @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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,8 +10,11 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import net.minecraft.server.v1_8_R3.IntHashMap;
|
import net.minecraft.server.v1_8_R3.IntHashMap;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.DyeColor;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -36,18 +39,6 @@ public class StreamSerializerTest {
|
|||||||
assertEquals(IntHashMap.class, MinecraftReflection.getIntHashMapClass());
|
assertEquals(IntHashMap.class, MinecraftReflection.getIntHashMapClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializer() throws IOException {
|
|
||||||
ItemStack before = new ItemStack(Material.GOLD_AXE);
|
|
||||||
|
|
||||||
StreamSerializer serializer = new StreamSerializer();
|
|
||||||
String data = serializer.serializeItemStack(before);
|
|
||||||
ItemStack after = serializer.deserializeItemStack(data);
|
|
||||||
|
|
||||||
assertEquals(before.getType(), after.getType());
|
|
||||||
assertEquals(before.getAmount(), after.getAmount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStrings() throws IOException {
|
public void testStrings() throws IOException {
|
||||||
StreamSerializer serializer = new StreamSerializer();
|
StreamSerializer serializer = new StreamSerializer();
|
||||||
@ -57,8 +48,7 @@ public class StreamSerializerTest {
|
|||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
serializer.serializeString(new DataOutputStream(buffer), initial);
|
serializer.serializeString(new DataOutputStream(buffer), initial);
|
||||||
|
|
||||||
DataInputStream input = new DataInputStream(
|
DataInputStream input = new DataInputStream(new ByteArrayInputStream(buffer.toByteArray()));
|
||||||
new ByteArrayInputStream(buffer.toByteArray()));
|
|
||||||
String deserialized = serializer.deserializeString(input, 50);
|
String deserialized = serializer.deserializeString(input, 50);
|
||||||
|
|
||||||
assertEquals(initial, deserialized);
|
assertEquals(initial, deserialized);
|
||||||
@ -76,8 +66,7 @@ public class StreamSerializerTest {
|
|||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
serializer.serializeCompound(new DataOutputStream(buffer), initial);
|
serializer.serializeCompound(new DataOutputStream(buffer), initial);
|
||||||
|
|
||||||
DataInputStream input = new DataInputStream(
|
DataInputStream input = new DataInputStream(new ByteArrayInputStream(buffer.toByteArray()));
|
||||||
new ByteArrayInputStream(buffer.toByteArray()));
|
|
||||||
NbtCompound deserialized = serializer.deserializeCompound(input);
|
NbtCompound deserialized = serializer.deserializeCompound(input);
|
||||||
|
|
||||||
assertEquals(initial, deserialized);
|
assertEquals(initial, deserialized);
|
||||||
@ -88,12 +77,23 @@ public class StreamSerializerTest {
|
|||||||
StreamSerializer serializer = new StreamSerializer();
|
StreamSerializer serializer = new StreamSerializer();
|
||||||
ItemStack initial = new ItemStack(Material.STRING);
|
ItemStack initial = new ItemStack(Material.STRING);
|
||||||
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
String serialized = serializer.serializeItemStack(initial);
|
||||||
serializer.serializeItemStack(new DataOutputStream(buffer), initial);
|
ItemStack deserialized = serializer.deserializeItemStack(serialized);
|
||||||
|
|
||||||
DataInputStream input = new DataInputStream(
|
assertEquals(initial, deserialized);
|
||||||
new ByteArrayInputStream(buffer.toByteArray()));
|
}
|
||||||
ItemStack deserialized = serializer.deserializeItemStack(input);
|
|
||||||
|
@Test
|
||||||
|
public void testItemMeta() throws IOException {
|
||||||
|
StreamSerializer serializer = new StreamSerializer();
|
||||||
|
ItemStack initial = new ItemStack(Material.WOOL, 2, DyeColor.BLUE.getWoolData());
|
||||||
|
|
||||||
|
ItemMeta meta = initial.getItemMeta();
|
||||||
|
meta.setDisplayName(ChatColor.BLUE + "Blue Wool");
|
||||||
|
initial.setItemMeta(meta);
|
||||||
|
|
||||||
|
String serialized = serializer.serializeItemStack(initial);
|
||||||
|
ItemStack deserialized = serializer.deserializeItemStack(serialized);
|
||||||
|
|
||||||
assertEquals(initial, deserialized);
|
assertEquals(initial, deserialized);
|
||||||
}
|
}
|
||||||
|
@ -75,4 +75,9 @@ public class ShadedByteBuf implements WrappedByteBuf {
|
|||||||
public void writeBytes(byte[] bytes) {
|
public void writeBytes(byte[] bytes) {
|
||||||
handle.get().writeBytes(bytes);
|
handle.get().writeBytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] array() {
|
||||||
|
return handle.get().array();
|
||||||
|
}
|
||||||
}
|
}
|
@ -55,11 +55,6 @@ public class ShadedNetty implements NettyCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public WrappedByteBuf allocateUnpooled() {
|
|
||||||
return new ShadedByteBuf(UnpooledByteBufAllocator.DEFAULT.buffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getGenericFutureListenerArray() {
|
public Class<?> getGenericFutureListenerArray() {
|
||||||
return GenericFutureListener[].class;
|
return GenericFutureListener[].class;
|
||||||
@ -97,4 +92,14 @@ public class ShadedNetty implements NettyCompat {
|
|||||||
public WrappedByteBuf packetWriter(DataOutputStream output) {
|
public WrappedByteBuf packetWriter(DataOutputStream output) {
|
||||||
return new ShadedByteBuf(ShadedByteBufAdapter.packetWriter(output));
|
return new ShadedByteBuf(ShadedByteBufAdapter.packetWriter(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WrappedByteBuf copiedBuffer(byte[] array) {
|
||||||
|
return new ShadedByteBuf(Unpooled.copiedBuffer(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WrappedByteBuf buffer() {
|
||||||
|
return new ShadedByteBuf(Unpooled.buffer());
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user