From f94b060591eb48ba8da5c566870652e6c8906f60 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 4 Dec 2013 19:19:02 +0100 Subject: [PATCH] Ensure StreamSerializer functions correctly in 1.7.2 --- .../protocol/reflect/ClassAnalyser.java | 85 ++++ .../protocol/reflect/FuzzyReflection.java | 29 +- .../protocol/utility/ByteBufAdapter.java | 376 ++++++++++++++++++ .../protocol/utility/StreamSerializer.java | 239 ++++++----- 4 files changed, 638 insertions(+), 91 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java new file mode 100644 index 00000000..03c62442 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java @@ -0,0 +1,85 @@ +package com.comphenix.protocol.reflect; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.List; + +import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor; +import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor; +import com.google.common.collect.Lists; + +import net.sf.cglib.asm.ClassReader; +import net.sf.cglib.asm.MethodVisitor; +import net.sf.cglib.asm.Type; + +public class ClassAnalyser { + /** + * Represents a method in ASM. + * @author Kristian + */ + public static class AsmMethod { + private final String ownerClass; + private final String methodName; + private final String signature; + + public AsmMethod(String ownerClass, String methodName, String signature) { + this.ownerClass = ownerClass; + this.methodName = methodName; + this.signature = signature; + } + + public String getOwnerClass() { + return ownerClass; + } + + public String getMethodName() { + return methodName; + } + + public String getSignature() { + return signature; + } + } + private static final ClassAnalyser DEFAULT = new ClassAnalyser(); + + /** + * Retrieve the default instance. + * @return The default. + */ + public static ClassAnalyser getDefault() { + return DEFAULT; + } + + /** + * Retrieve every method calls in the given method. + * @param method - the method to analyse. + * @return The method calls. + * @throws IOException Cannot access the parent class. + */ + public List getMethodCalls(Method method) throws IOException { + final ClassReader reader = new ClassReader(method.getDeclaringClass().getCanonicalName()); + final List output = Lists.newArrayList(); + + // The method we are looking for + final String methodName = method.getName(); + final String methodDescription = Type.getMethodDescriptor(method); + + reader.accept(new EmptyClassVisitor() { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + // Check method + if (methodName.equals(name) && methodDescription.equals(desc)) { + return new EmptyMethodVisitor() { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + output.add(new AsmMethod(owner, name, desc)); + } + }; + } + return null; + } + + }, ClassReader.EXPAND_FRAMES); + return output; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 04bf8991..68879b39 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -53,6 +53,13 @@ public class FuzzyReflection { * @throws IllegalStateException If the current security context prohibits reflection. */ public Object get(Object instance); + + /** + * Set the value of a field for a particular instance. + * @param instance - the instance, or NULL for a static field. + * @param value - the new value of the field. + */ + public void set(Object instance, Object value); } /** @@ -129,7 +136,6 @@ public class FuzzyReflection { public static FieldAccessor getFieldAccessor(Class instanceClass, Class fieldClass, boolean forceAccess) { // Get a field accessor Field field = FuzzyReflection.fromObject(instanceClass, forceAccess).getFieldByType(null, fieldClass); - field.setAccessible(true); return getFieldAccessor(field); } @@ -139,6 +145,18 @@ public class FuzzyReflection { * @return The field accessor. */ public static FieldAccessor getFieldAccessor(final Field field) { + return getFieldAccessor(field, true); + } + + /** + * Retrieve a field accessor from a given field that uses unchecked exceptions. + * @param field - the field. + * @param forceAccess - whether or not to skip Java access checking. + * @return The field accessor. + */ + public static FieldAccessor getFieldAccessor(final Field field, boolean forceAccess) { + field.setAccessible(true); + return new FieldAccessor() { @Override public Object get(Object instance) { @@ -148,6 +166,15 @@ public class FuzzyReflection { throw new IllegalStateException("Cannot use reflection.", e); } } + + @Override + public void set(Object instance, Object value) { + try { + field.set(instance, value); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Cannot use reflection.", e); + } + } }; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java new file mode 100644 index 00000000..858d391e --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java @@ -0,0 +1,376 @@ +package com.comphenix.protocol.utility; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.Channels; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.channels.WritableByteChannel; + +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; +import com.google.common.io.ByteStreams; +import com.google.common.io.LimitInputStream; + +import net.minecraft.util.io.netty.buffer.AbstractByteBuf; +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.ByteBufAllocator; + +/** + * Construct a ByteBuf around an input stream and an output stream. + *

+ * Note that as streams usually don't support seeking, this implementation will ignore + * all indexing in the byte buffer. + * @author Kristian + */ +class ByteBufAdapter extends AbstractByteBuf { + private DataInputStream input; + private DataOutputStream output; + + // For modifying the reader or writer index + private static FieldAccessor READER_INDEX; + private static FieldAccessor WRITER_INDEX; + + private static final int CAPACITY = Short.MAX_VALUE; + + private ByteBufAdapter(DataInputStream input, DataOutputStream output) { + // Just pick a figure + super(CAPACITY); + this.input = input; + this.output = output; + + // Prepare accessors + try { + if (READER_INDEX == null) { + READER_INDEX = FuzzyReflection.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("readerIndex")); + } + if (WRITER_INDEX == null) { + WRITER_INDEX = FuzzyReflection.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("writerIndex")); + } + } catch (Exception e) { + throw new RuntimeException("Cannot initialize ByteBufAdapter.", e); + } + + // "Infinite" reading/writing + if (input == null) + READER_INDEX.set(this, Integer.MAX_VALUE); + if (output == null) + WRITER_INDEX.set(this, Integer.MAX_VALUE); + } + + /** + * Construct a new Minecraft packet serializer using the current byte buf adapter. + * @param input - the input stream. + * @return A packet serializer with a wrapped byte buf adapter. + */ + public static ByteBuf packetReader(DataInputStream input) { + return MinecraftReflection.getPacketDataSerializer(new ByteBufAdapter(input, null)); + } + + /** + * Construct a new Minecraft packet deserializer using the current byte buf adapter. + * @param output - the output stream. + * @return A packet serializer with a wrapped byte buf adapter. + */ + public static ByteBuf packetWriter(DataOutputStream output) { + return MinecraftReflection.getPacketDataSerializer(new ByteBufAdapter(null, output)); + } + + @Override + public int refCnt() { + return 1; + } + + @Override + public boolean release() { + return false; + } + + @Override + public boolean release(int paramInt) { + return false; + } + + @Override + protected byte _getByte(int paramInt) { + try { + return input.readByte(); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + } + + @Override + protected short _getShort(int paramInt) { + try { + return input.readShort(); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + } + + @Override + protected int _getUnsignedMedium(int paramInt) { + try { + return input.readUnsignedShort(); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + } + + @Override + protected int _getInt(int paramInt) { + try { + return input.readInt(); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + } + + @Override + protected long _getLong(int paramInt) { + try { + return input.readLong(); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + } + + @Override + protected void _setByte(int index, int value) { + try { + output.writeByte(value); + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + protected void _setShort(int index, int value) { + try { + output.writeShort(value); + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + protected void _setMedium(int index, int value) { + try { + output.writeShort(value); + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + protected void _setInt(int index, int value) { + try { + output.writeInt(value); + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + protected void _setLong(int index, long value) { + try { + output.writeLong(value); + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + public int capacity() { + return CAPACITY; + } + + @Override + public ByteBuf capacity(int paramInt) { + return this; + } + + @Override + public ByteBufAllocator alloc() { + return null; + } + + @Override + public ByteOrder order() { + return ByteOrder.LITTLE_ENDIAN; + } + + @Override + public ByteBuf unwrap() { + return null; + } + + @Override + public boolean isDirect() { + return false; + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { + try { + for (int i = 0; i < length; i++) { + dst.setByte(dstIndex + i, input.read()); + } + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + return this; + } + + @Override + public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { + try { + input.read(dst, dstIndex, length); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + return this; + } + + @Override + public ByteBuf getBytes(int index, ByteBuffer dst) { + try { + dst.put(ByteStreams.toByteArray(input)); + } catch (IOException e) { + throw new RuntimeException("Cannot read input.", e); + } + return this; + } + + @Override + public ByteBuf getBytes(int index, OutputStream dst, int length) throws IOException { + ByteStreams.copy(new LimitInputStream(input, length), dst); + return this; + } + + @Override + public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { + byte[] data = ByteStreams.toByteArray(new LimitInputStream(input, length)); + + out.write(ByteBuffer.wrap(data)); + return data.length; + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { + byte[] buffer = new byte[length]; + src.getBytes(srcIndex, buffer); + + try { + output.write(buffer); + return this; + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { + try { + output.write(src, srcIndex, length); + return this; + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + public ByteBuf setBytes(int index, ByteBuffer src) { + try { + WritableByteChannel channel = Channels.newChannel(output); + + channel.write(src); + return this; + } catch (IOException e) { + throw new RuntimeException("Cannot write output.", e); + } + } + + @Override + public int setBytes(int index, InputStream in, int length) throws IOException { + LimitInputStream limit = new LimitInputStream(in, length); + ByteStreams.copy(limit, output); + return length - limit.available(); + } + + @Override + public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(length); + WritableByteChannel channel = Channels.newChannel(output); + + int count = in.read(buffer); + channel.write(buffer); + return count; + } + + @Override + public ByteBuf copy(int index, int length) { + throw new UnsupportedOperationException("Cannot seek in input stream."); + } + + @Override + public int nioBufferCount() { + return 0; + } + + @Override + public ByteBuffer nioBuffer(int paramInt1, int paramInt2) { + throw new UnsupportedOperationException(); + } + + @Override + public ByteBuffer internalNioBuffer(int paramInt1, int paramInt2) { + return null; + } + + @Override + public ByteBuffer[] nioBuffers(int paramInt1, int paramInt2) { + return null; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public byte[] array() { + return null; + } + + @Override + public int arrayOffset() { + return 0; + } + + @Override + public boolean hasMemoryAddress() { + return false; + } + + @Override + public long memoryAddress() { + return 0; + } + + @Override + public ByteBuf retain(int paramInt) { + return this; + } + + @Override + public ByteBuf retain() { + return this; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java index 49c21c80..de40185e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -14,6 +14,7 @@ import org.bukkit.inventory.ItemStack; import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; @@ -25,14 +26,14 @@ import com.comphenix.protocol.wrappers.nbt.NbtFactory; */ public class StreamSerializer { // Cached methods - private static Method READ_ITEM_METHOD; - private static Method WRITE_ITEM_METHOD; + private static MethodAccessor READ_ITEM_METHOD; + private static MethodAccessor WRITE_ITEM_METHOD; - private static Method READ_NBT_METHOD; - private static Method WRITE_NBT_METHOD; + private static MethodAccessor READ_NBT_METHOD; + private static MethodAccessor WRITE_NBT_METHOD; - private static Method READ_STRING_METHOD; - private static Method WRITE_STRING_METHOD; + private static MethodAccessor READ_STRING_METHOD; + private static MethodAccessor WRITE_STRING_METHOD; /** * Read or deserialize an item stack from an underlying input stream. @@ -47,26 +48,37 @@ public class StreamSerializer { public ItemStack deserializeItemStack(@Nonnull DataInputStream input) throws IOException { if (input == null) throw new IllegalArgumentException("Input stream cannot be NULL."); - if (READ_ITEM_METHOD == null) { - READ_ITEM_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( - FuzzyMethodContract.newBuilder(). - parameterCount(1). - parameterDerivedOf(DataInput.class). - returnDerivedOf(MinecraftReflection.getItemStackClass()). - build()); - } - try { - Object nmsItem = READ_ITEM_METHOD.invoke(null, input); + Object nmsItem = null; + + if (MinecraftReflection.isUsingNetty()) { + if (READ_ITEM_METHOD == null) { + READ_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("readItemStack", + MinecraftReflection.getItemStackClass(), new Class[0]) + ); + } + nmsItem = READ_ITEM_METHOD.invoke(ByteBufAdapter.packetReader(input)); - // Convert back to a Bukkit item stack - if (nmsItem != null) - return MinecraftReflection.getBukkitItemStack(nmsItem); - else - return null; - - } catch (Exception e) { - throw new IOException("Cannot read item stack.", e); + } else { + if (READ_ITEM_METHOD == null) { + READ_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( + FuzzyMethodContract.newBuilder(). + parameterCount(1). + parameterDerivedOf(DataInput.class). + returnDerivedOf(MinecraftReflection.getItemStackClass()). + build()) + ); + } + nmsItem = READ_ITEM_METHOD.invoke(null, input); } + + // Convert back to a Bukkit item stack + if (nmsItem != null) + return MinecraftReflection.getBukkitItemStack(nmsItem); + else + return null; } /** @@ -78,26 +90,43 @@ public class StreamSerializer { 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( + Object nmsCompound = null; + + // Invoke the correct method + if (MinecraftReflection.isUsingNetty()) { + if (READ_NBT_METHOD == null) { + READ_NBT_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("readNbtCompound", + MinecraftReflection.getNBTCompoundClass(), new Class[0]) + ); + } + nmsCompound = READ_NBT_METHOD.invoke(ByteBufAdapter.packetReader(input)); + + } else { + if (READ_NBT_METHOD == null) { + READ_NBT_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(1). parameterDerivedOf(DataInput.class). returnDerivedOf(MinecraftReflection.getNBTBaseClass()). - build()); - } - try { - Object nmsCompound = READ_NBT_METHOD.invoke(null, input); + build()) + ); + } - // Convert back to an NBT Compound - if (nmsCompound != null) - return NbtFactory.fromNMSCompound(nmsCompound); - else - return null; - - } catch (Exception e) { - throw new IOException("Cannot read item stack.", e); + try { + nmsCompound = READ_NBT_METHOD.invoke(null, input); + } catch (Exception e) { + throw new IOException("Cannot read item stack.", e); + } } + + // Convert back to an NBT Compound + if (nmsCompound != null) + return NbtFactory.fromNMSCompound(nmsCompound); + else + return null; } /** @@ -117,21 +146,28 @@ public class StreamSerializer { 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 + if (MinecraftReflection.isUsingNetty()) { + if (READ_STRING_METHOD == null) { + READ_STRING_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("readString", String.class, new Class[] { int.class }) + ); + } + return (String) READ_STRING_METHOD.invoke(ByteBufAdapter.packetReader(input), maximumLength); + + } else { + if (READ_STRING_METHOD == null) { + READ_STRING_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( + FuzzyMethodContract.newBuilder(). + parameterCount(2). + parameterDerivedOf(DataInput.class, 0). + parameterExactType(int.class, 1). + returnTypeExact(String.class). + build()) + ); + } return (String) READ_STRING_METHOD.invoke(null, input, maximumLength); - } catch (Exception e) { - throw new IOException("Cannot read Minecraft string.", e); } } @@ -168,17 +204,26 @@ public class StreamSerializer { // Get the NMS version of the ItemStack Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack); - if (WRITE_ITEM_METHOD == null) - WRITE_ITEM_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( - FuzzyMethodContract.newBuilder(). - parameterCount(2). - parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0). - parameterDerivedOf(DataOutput.class, 1). - build()); - try { + if (MinecraftReflection.isUsingNetty()) { + if (WRITE_ITEM_METHOD == null) { + WRITE_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("writeStack", MinecraftReflection.getItemStackClass()) + ); + } + WRITE_ITEM_METHOD.invoke(ByteBufAdapter.packetWriter(output), nmsItem); + + } else { + if (WRITE_ITEM_METHOD == null) + WRITE_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( + FuzzyMethodContract.newBuilder(). + parameterCount(2). + parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0). + parameterDerivedOf(DataOutput.class, 1). + build()) + ); WRITE_ITEM_METHOD.invoke(null, nmsItem, output); - } catch (Exception e) { - throw new IOException("Cannot write item stack " + stack, e); } } @@ -198,21 +243,28 @@ public class StreamSerializer { // 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 { + if (MinecraftReflection.isUsingNetty()) { + if (WRITE_NBT_METHOD == null) { + WRITE_NBT_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()) + ); + } + WRITE_NBT_METHOD.invoke(ByteBufAdapter.packetWriter(output), handle); + + } else { + if (WRITE_NBT_METHOD == null) { + WRITE_NBT_METHOD = FuzzyReflection.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); - } catch (Exception e) { - throw new IOException("Cannot write compound " + compound, e); } } @@ -230,21 +282,28 @@ public class StreamSerializer { 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 + if (MinecraftReflection.isUsingNetty()) { + if (WRITE_STRING_METHOD == null) { + WRITE_STRING_METHOD = FuzzyReflection.getMethodAccessor( + FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). + getMethodByParameters("writeString", String.class) + ); + } + WRITE_STRING_METHOD.invoke(ByteBufAdapter.packetWriter(output), text); + + } else { + if (WRITE_STRING_METHOD == null) { + WRITE_STRING_METHOD = FuzzyReflection.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); - } catch (Exception e) { - throw new IOException("Cannot read Minecraft string.", e); } }