Archiviert
13
0

Ensure StreamSerializer functions correctly in 1.7.2

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-12-04 19:19:02 +01:00
Ursprung 7055cadaef
Commit f94b060591
4 geänderte Dateien mit 638 neuen und 91 gelöschten Zeilen

Datei anzeigen

@ -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<AsmMethod> getMethodCalls(Method method) throws IOException {
final ClassReader reader = new ClassReader(method.getDeclaringClass().getCanonicalName());
final List<AsmMethod> 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;
}
}

Datei anzeigen

@ -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);
}
}
};
}

Datei anzeigen

@ -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.
* <p>
* 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;
}
}

Datei anzeigen

@ -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.");
Object nmsItem = null;
if (MinecraftReflection.isUsingNetty()) {
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
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));
} 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());
build())
);
}
nmsItem = READ_ITEM_METHOD.invoke(null, input);
}
try {
Object nmsItem = READ_ITEM_METHOD.invoke(null, 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);
}
}
/**
@ -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.");
Object nmsCompound = null;
// Invoke the correct method
if (MinecraftReflection.isUsingNetty()) {
if (READ_NBT_METHOD == null) {
READ_NBT_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
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());
build())
);
}
try {
Object nmsCompound = READ_NBT_METHOD.invoke(null, input);
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;
} catch (Exception e) {
throw new IOException("Cannot read item stack.", e);
}
}
/**
@ -117,21 +146,28 @@ public class StreamSerializer {
if (maximumLength < 0)
throw new IllegalArgumentException("Maximum lenght cannot be negative.");
if (MinecraftReflection.isUsingNetty()) {
if (READ_STRING_METHOD == null) {
READ_STRING_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
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());
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);
}
}
@ -168,17 +204,26 @@ public class StreamSerializer {
// Get the NMS version of the ItemStack
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
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.fromClass(MinecraftReflection.getPacketClass()).getMethod(
WRITE_ITEM_METHOD = FuzzyReflection.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
parameterDerivedOf(DataOutput.class, 1).
build());
try {
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 (MinecraftReflection.isUsingNetty()) {
if (WRITE_NBT_METHOD == null) {
WRITE_NBT_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
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.setAccessible(true);
build())
);
}
try {
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 (MinecraftReflection.isUsingNetty()) {
if (WRITE_STRING_METHOD == null) {
WRITE_STRING_METHOD = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
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());
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);
}
}