Add support for serializing and deserializing NBT tags in 1.7.2
Dieser Commit ist enthalten in:
Ursprung
65e665aa59
Commit
398b1bc3be
@ -1,5 +1,7 @@
|
|||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
@ -27,6 +29,20 @@ abstract class ClassSource {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a class source from a mapping of canonical names and the corresponding classes.
|
||||||
|
* @param map - map of class names and classes.
|
||||||
|
* @return The class source.
|
||||||
|
*/
|
||||||
|
public static ClassSource fromMap(final Map<String, Class<?>> map) {
|
||||||
|
return new ClassSource() {
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||||
|
return map.get(canonicalName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a class by name.
|
* Retrieve a class by name.
|
||||||
* @param canonicalName - the full canonical name of the class.
|
* @param canonicalName - the full canonical name of the class.
|
||||||
|
@ -33,6 +33,8 @@ import java.util.Set;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javassist.bytecode.CodeAttribute.RuntimeCopyException;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
@ -51,7 +53,9 @@ import com.comphenix.protocol.error.Report;
|
|||||||
import com.comphenix.protocol.error.ReportType;
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
|
import com.comphenix.protocol.reflect.ClassAnalyser;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod;
|
||||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
||||||
@ -107,8 +111,9 @@ public class MinecraftReflection {
|
|||||||
private static String MINECRAFT_FULL_PACKAGE = null;
|
private static String MINECRAFT_FULL_PACKAGE = null;
|
||||||
private static String CRAFTBUKKIT_PACKAGE = null;
|
private static String CRAFTBUKKIT_PACKAGE = null;
|
||||||
|
|
||||||
private static CachedPackage minecraftPackage;
|
// Package private for the purpose of unit testing
|
||||||
private static CachedPackage craftbukkitPackage;
|
static CachedPackage minecraftPackage;
|
||||||
|
static CachedPackage craftbukkitPackage;
|
||||||
|
|
||||||
// org.bukkit.craftbukkit
|
// org.bukkit.craftbukkit
|
||||||
private static Constructor<?> craftNMSConstructor;
|
private static Constructor<?> craftNMSConstructor;
|
||||||
@ -561,22 +566,35 @@ public class MinecraftReflection {
|
|||||||
try {
|
try {
|
||||||
return getMinecraftClass("Packet");
|
return getMinecraftClass("Packet");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
FuzzyClassContract paketContract = null;
|
||||||
|
|
||||||
// What kind of class we're looking for (sanity check)
|
// What kind of class we're looking for (sanity check)
|
||||||
FuzzyClassContract paketContract =
|
if (isUsingNetty()) {
|
||||||
FuzzyClassContract.newBuilder().
|
paketContract = FuzzyClassContract.newBuilder().
|
||||||
field(FuzzyFieldContract.newBuilder().
|
|
||||||
typeDerivedOf(Map.class).
|
|
||||||
requireModifier(Modifier.STATIC)).
|
|
||||||
field(FuzzyFieldContract.newBuilder().
|
|
||||||
typeDerivedOf(Set.class).
|
|
||||||
requireModifier(Modifier.STATIC)).
|
|
||||||
method(FuzzyMethodContract.newBuilder().
|
method(FuzzyMethodContract.newBuilder().
|
||||||
parameterSuperOf(DataInputStream.class).
|
parameterDerivedOf(ByteBuf.class).
|
||||||
returnTypeVoid()).
|
returnTypeVoid()).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterDerivedOf(ByteBuf.class, 0).
|
||||||
|
parameterExactType(byte[].class, 1).
|
||||||
|
returnTypeVoid()).
|
||||||
|
build();
|
||||||
|
} else {
|
||||||
|
paketContract = FuzzyClassContract.newBuilder().
|
||||||
|
field(FuzzyFieldContract.newBuilder().
|
||||||
|
typeDerivedOf(Map.class).
|
||||||
|
requireModifier(Modifier.STATIC)).
|
||||||
|
field(FuzzyFieldContract.newBuilder().
|
||||||
|
typeDerivedOf(Set.class).
|
||||||
|
requireModifier(Modifier.STATIC)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterSuperOf(DataInputStream.class).
|
||||||
|
returnTypeVoid()).
|
||||||
build();
|
build();
|
||||||
|
}
|
||||||
|
|
||||||
// Select a method with one Minecraft object parameter
|
// Select a method with one Minecraft object parameter
|
||||||
Method selected = FuzzyReflection.fromClass(getNetHandlerClass()).
|
Method selected = FuzzyReflection.fromClass(getNetServerHandlerClass()).
|
||||||
getMethod(FuzzyMethodContract.newBuilder().
|
getMethod(FuzzyMethodContract.newBuilder().
|
||||||
parameterMatches(paketContract, 0).
|
parameterMatches(paketContract, 0).
|
||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
@ -715,11 +733,39 @@ public class MinecraftReflection {
|
|||||||
try {
|
try {
|
||||||
return getMinecraftClass("NetServerHandler", "PlayerConnection");
|
return getMinecraftClass("NetServerHandler", "PlayerConnection");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
// Use the player connection field
|
try {
|
||||||
return setMinecraftClass("NetServerHandler",
|
// Use the player connection field
|
||||||
FuzzyReflection.fromClass(getEntityPlayerClass()).
|
return setMinecraftClass("NetServerHandler",
|
||||||
getFieldByType("playerConnection", getNetHandlerClass()).getType()
|
FuzzyReflection.fromClass(getEntityPlayerClass()).
|
||||||
);
|
getFieldByType("playerConnection", getNetHandlerClass()).getType()
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (RuntimeException e1) {
|
||||||
|
// Okay, this must be on 1.7.2
|
||||||
|
Class<?> playerClass = getEntityPlayerClass();
|
||||||
|
|
||||||
|
FuzzyClassContract playerConnection = FuzzyClassContract.newBuilder().
|
||||||
|
field(FuzzyFieldContract.newBuilder().typeExact(playerClass).build()).
|
||||||
|
constructor(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(3).
|
||||||
|
parameterSuperOf(getMinecraftServerClass(), 0).
|
||||||
|
parameterSuperOf(getEntityPlayerClass(), 2).
|
||||||
|
build()
|
||||||
|
).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterCount(1).
|
||||||
|
parameterExactType(String.class).
|
||||||
|
build()
|
||||||
|
).
|
||||||
|
build();
|
||||||
|
|
||||||
|
// If not, use duck typing
|
||||||
|
Class<?> fieldType = FuzzyReflection.fromClass(getEntityPlayerClass(), true).getField(
|
||||||
|
FuzzyFieldContract.newBuilder().typeMatches(playerConnection).build()
|
||||||
|
).getType();
|
||||||
|
|
||||||
|
return setMinecraftClass("NetServerHandler", fieldType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,6 +797,7 @@ public class MinecraftReflection {
|
|||||||
try {
|
try {
|
||||||
return getMinecraftClass("NetHandler", "Connection");
|
return getMinecraftClass("NetHandler", "Connection");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
// Try getting the net login handler
|
||||||
return setMinecraftClass("NetHandler", getNetLoginHandlerClass().getSuperclass());
|
return setMinecraftClass("NetHandler", getNetLoginHandlerClass().getSuperclass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -948,23 +995,46 @@ public class MinecraftReflection {
|
|||||||
try {
|
try {
|
||||||
return getMinecraftClass("NBTBase");
|
return getMinecraftClass("NBTBase");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
FuzzyClassContract tagCompoundContract = FuzzyClassContract.newBuilder().
|
Class<?> nbtBase = null;
|
||||||
constructor(FuzzyMethodContract.newBuilder().
|
|
||||||
parameterExactType(String.class).
|
|
||||||
parameterCount(1)).
|
|
||||||
field(FuzzyFieldContract.newBuilder().
|
|
||||||
typeDerivedOf(Map.class)).
|
|
||||||
build();
|
|
||||||
|
|
||||||
Method selected = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).
|
if (isUsingNetty()) {
|
||||||
|
FuzzyClassContract tagCompoundContract = FuzzyClassContract.newBuilder().
|
||||||
|
field(FuzzyFieldContract.newBuilder().
|
||||||
|
typeDerivedOf(Map.class)).
|
||||||
|
method(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterDerivedOf(DataOutput.class).
|
||||||
|
parameterCount(1)).
|
||||||
|
build();
|
||||||
|
|
||||||
|
Method selected = FuzzyReflection.fromClass(getPacketDataSerializerClass()).
|
||||||
|
getMethod(FuzzyMethodContract.newBuilder().
|
||||||
|
banModifier(Modifier.STATIC).
|
||||||
|
parameterCount(1).
|
||||||
|
parameterMatches(tagCompoundContract).
|
||||||
|
returnTypeVoid().
|
||||||
|
build()
|
||||||
|
);
|
||||||
|
nbtBase = selected.getParameterTypes()[0].getSuperclass();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
FuzzyClassContract tagCompoundContract = FuzzyClassContract.newBuilder().
|
||||||
|
constructor(FuzzyMethodContract.newBuilder().
|
||||||
|
parameterExactType(String.class).
|
||||||
|
parameterCount(1)).
|
||||||
|
field(FuzzyFieldContract.newBuilder().
|
||||||
|
typeDerivedOf(Map.class)).
|
||||||
|
build();
|
||||||
|
|
||||||
|
Method selected = FuzzyReflection.fromClass(getPacketClass()).
|
||||||
getMethod(FuzzyMethodContract.newBuilder().
|
getMethod(FuzzyMethodContract.newBuilder().
|
||||||
requireModifier(Modifier.STATIC).
|
requireModifier(Modifier.STATIC).
|
||||||
parameterSuperOf(DataInputStream.class).
|
parameterSuperOf(DataInputStream.class).
|
||||||
parameterCount(1).
|
parameterCount(1).
|
||||||
returnTypeMatches(tagCompoundContract).
|
returnTypeMatches(tagCompoundContract).
|
||||||
build()
|
build()
|
||||||
);
|
);
|
||||||
Class<?> nbtBase = selected.getReturnType().getSuperclass();
|
nbtBase = selected.getReturnType().getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
// That can't be correct
|
// That can't be correct
|
||||||
if (nbtBase == null || nbtBase.equals(Object.class)) {
|
if (nbtBase == null || nbtBase.equals(Object.class)) {
|
||||||
@ -1201,6 +1271,36 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the NBTCompressedStreamTools class.
|
||||||
|
* @return The NBTCompressedStreamTools class.
|
||||||
|
*/
|
||||||
|
public static Class<?> getNbtCompressedStreamToolsClass() {
|
||||||
|
try {
|
||||||
|
return getMinecraftClass("NBTCompressedStreamTools");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Class<?> packetSerializer = getPacketDataSerializerClass();
|
||||||
|
|
||||||
|
// Get the write NBT compound method
|
||||||
|
Method writeNbt = FuzzyReflection.fromClass(packetSerializer).
|
||||||
|
getMethodByParameters("writeNbt", getNBTCompoundClass());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Now -- we inspect all the method calls within that method, and use the first foreign Minecraft class
|
||||||
|
for (AsmMethod method : ClassAnalyser.getDefault().getMethodCalls(writeNbt)) {
|
||||||
|
Class<?> owner = MinecraftReflection.class.getClassLoader().loadClass(method.getOwnerClass().replace('/', '.'));
|
||||||
|
|
||||||
|
if (!packetSerializer.equals(owner) && isMinecraftClass(owner)) {
|
||||||
|
return setMinecraftClass("NBTCompressedStreamTools", owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e1) {
|
||||||
|
throw new RuntimeException("Unable to analyse class.", e1);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unable to find NBTCompressedStreamTools.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an instance of the packet data serializer wrapper.
|
* Retrieve an instance of the packet data serializer wrapper.
|
||||||
* @param buffer - the buffer.
|
* @param buffer - the buffer.
|
||||||
|
@ -33,8 +33,9 @@ public class NbtBinarySerializer {
|
|||||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||||
|
|
||||||
// Use the base class
|
// Use the base class
|
||||||
methodWrite = FuzzyReflection.fromClass(base).
|
methodWrite = getUtilityClass().
|
||||||
getMethodByParameters("writeNBT", base, DataOutput.class);
|
getMethodByParameters("writeNBT", base, DataOutput.class);
|
||||||
|
methodWrite.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -52,19 +53,37 @@ public class NbtBinarySerializer {
|
|||||||
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
|
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
|
||||||
if (methodLoad == null) {
|
if (methodLoad == null) {
|
||||||
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
Class<?> base = MinecraftReflection.getNBTBaseClass();
|
||||||
|
Class<?>[] params = MinecraftReflection.isUsingNetty() ?
|
||||||
|
new Class<?>[] { DataInput.class, int.class } :
|
||||||
|
new Class<?>[] { DataInput.class };
|
||||||
|
|
||||||
// Use the base class
|
// Use the base class
|
||||||
methodLoad = FuzzyReflection.fromClass(base).
|
methodLoad = getUtilityClass().getMethodByParameters("load", base, params);
|
||||||
getMethodByParameters("load", base, new Class<?>[] { DataInput.class });
|
methodLoad.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return NbtFactory.fromNMS(methodLoad.invoke(null, source), null);
|
Object result = null;
|
||||||
|
|
||||||
|
// Invoke the correct utility method
|
||||||
|
if (MinecraftReflection.isUsingNetty())
|
||||||
|
result = methodLoad.invoke(null, source, 0);
|
||||||
|
else
|
||||||
|
result = methodLoad.invoke(null, source);
|
||||||
|
return NbtFactory.fromNMS(result, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new FieldAccessException("Unable to read NBT from " + source, e);
|
throw new FieldAccessException("Unable to read NBT from " + source, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FuzzyReflection getUtilityClass() {
|
||||||
|
if (MinecraftReflection.isUsingNetty()) {
|
||||||
|
return FuzzyReflection.fromClass(MinecraftReflection.getNbtCompressedStreamToolsClass(), true);
|
||||||
|
} else {
|
||||||
|
return FuzzyReflection.fromClass(MinecraftReflection.getNBTBaseClass(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load an NBT compound from a stream.
|
* Load an NBT compound from a stream.
|
||||||
* @param source - the input stream.
|
* @param source - the input stream.
|
||||||
|
@ -56,7 +56,6 @@ import com.comphenix.protocol.wrappers.WrappedWatchableObject;
|
|||||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
// Ensure that the CraftItemFactory is mockable
|
// Ensure that the CraftItemFactory is mockable
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren