Archiviert
13
0

Adding partial support for MCPC-Plus 1.7.2.

This doesn't include handling the different package names of 
net.minecraft.util.io.netty in MCPC.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2014-05-03 19:13:20 +02:00
Ursprung 71ce362c8e
Commit 8a2e696363
4 geänderte Dateien mit 131 neuen und 29 gelöschten Zeilen

Datei anzeigen

@ -25,6 +25,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -84,13 +85,18 @@ public class MinecraftReflection {
public static final ReportType REPORT_CANNOT_LOAD_CPC_REMAPPER = new ReportType("Unable to load MCPC remapper."); public static final ReportType REPORT_CANNOT_LOAD_CPC_REMAPPER = new ReportType("Unable to load MCPC remapper.");
public static final ReportType REPORT_NON_CRAFTBUKKIT_LIBRARY_PACKAGE = new ReportType("Cannot find standard Minecraft library location. Assuming MCPC."); public static final ReportType REPORT_NON_CRAFTBUKKIT_LIBRARY_PACKAGE = new ReportType("Cannot find standard Minecraft library location. Assuming MCPC.");
/**
* Regular expression that matches a canonical Java class.
*/
private static final String CANONICAL_REGEX = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
/** /**
* Regular expression that matches a Minecraft object. * Regular expression that matches a Minecraft object.
* <p> * <p>
* Replaced by the method {@link #getMinecraftObjectRegex()}. * Replaced by the method {@link #getMinecraftObjectRegex()}.
*/ */
@Deprecated @Deprecated
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; public static final String MINECRAFT_OBJECT = "net\\.minecraft\\." + CANONICAL_REGEX;
/** /**
* Regular expression computed dynamically. * Regular expression computed dynamically.
@ -213,6 +219,15 @@ public class MinecraftReflection {
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE); Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
if (packageMatcher.matches()) { if (packageMatcher.matches()) {
packageVersion = packageMatcher.group(1); packageVersion = packageMatcher.group(1);
} else {
MinecraftVersion version = new MinecraftVersion(craftServer);
// See if we need a package version
if (MinecraftVersion.SCARY_UPDATE.compareTo(version) <= 0) {
// Just assume R1 - it's probably fine
packageVersion = "v" + version.getMajor() + "_" + version.getMinor() + "_R1";
System.err.println("[ProtocolLib] Assuming package version: " + packageVersion);
}
} }
// Libigot patch // Libigot patch
@ -241,7 +256,7 @@ public class MinecraftReflection {
// The package is usualy flat, so go with that assumption // The package is usualy flat, so go with that assumption
String matcher = String matcher =
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ? (MINECRAFT_PREFIX_PACKAGE.length() > 0 ?
Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + "\\w+"; Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
// We'll still accept the default location, however // We'll still accept the default location, however
setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_OBJECT + ")"); setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_OBJECT + ")");
@ -266,7 +281,7 @@ public class MinecraftReflection {
throw new IllegalStateException("Could not find Bukkit. Is it running?"); throw new IllegalStateException("Could not find Bukkit. Is it running?");
} }
} }
/** /**
* Retrieve the Minecraft library package string. * Retrieve the Minecraft library package string.
* @return The library package. * @return The library package.
@ -1249,11 +1264,11 @@ public class MinecraftReflection {
public static Class<?> getWatchableObjectClass() { public static Class<?> getWatchableObjectClass() {
try { try {
return getMinecraftClass("WatchableObject"); return getMinecraftClass("WatchableObject");
} catch (RuntimeException e) { } catch (RuntimeException e) {
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true). Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
getMethod(FuzzyMethodContract.newBuilder(). getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.STATIC). requireModifier(Modifier.STATIC).
parameterDerivedOf(DataOutput.class, 0). parameterDerivedOf(isUsingNetty() ? getPacketDataSerializerClass() : DataOutput.class, 0).
parameterMatches(getMinecraftObjectMatcher(), 1). parameterMatches(getMinecraftObjectMatcher(), 1).
build()); build());
@ -1270,19 +1285,37 @@ public class MinecraftReflection {
try { try {
return getMinecraftClass("ServerConnection"); return getMinecraftClass("ServerConnection");
} catch (RuntimeException e) { } catch (RuntimeException e) {
FuzzyClassContract serverConnectionContract = FuzzyClassContract.newBuilder(). Method selected = null;
FuzzyClassContract.Builder serverConnectionContract = FuzzyClassContract.newBuilder().
constructor(FuzzyMethodContract.newBuilder(). constructor(FuzzyMethodContract.newBuilder().
parameterExactType(getMinecraftServerClass()). parameterExactType(getMinecraftServerClass()).
parameterCount(1)). parameterCount(1));
method(FuzzyMethodContract.newBuilder().
parameterExactType(getNetServerHandlerClass())).
build();
Method selected = FuzzyReflection.fromClass(getMinecraftServerClass()). if (isUsingNetty()) {
getMethod(FuzzyMethodContract.newBuilder(). serverConnectionContract.
requireModifier(Modifier.ABSTRACT). method(FuzzyMethodContract.newBuilder().
returnTypeMatches(serverConnectionContract). parameterDerivedOf(InetAddress.class, 0).
build()); parameterDerivedOf(int.class, 1).
parameterCount(2)
);
selected = FuzzyReflection.fromClass(getMinecraftServerClass()).
getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.PUBLIC).
returnTypeMatches(serverConnectionContract.build()).
build());
} else {
serverConnectionContract.
method(FuzzyMethodContract.newBuilder().
parameterExactType(getNetServerHandlerClass()));
selected = FuzzyReflection.fromClass(getMinecraftServerClass()).
getMethod(FuzzyMethodContract.newBuilder().
requireModifier(Modifier.ABSTRACT).
returnTypeMatches(serverConnectionContract.build()).
build());
}
// Use the return type // Use the return type
return setMinecraftClass("ServerConnection", selected.getReturnType()); return setMinecraftClass("ServerConnection", selected.getReturnType());

Datei anzeigen

@ -98,7 +98,7 @@ class RemappedClassSource extends ClassSource {
* @param path - the canonical class name. * @param path - the canonical class name.
* @return The obfuscated class name. * @return The obfuscated class name.
*/ */
private String getClassName(String path) { public String getClassName(String path) {
try { try {
String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/')); String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/'));
return remapped.replace('/', '.'); return remapped.replace('/', '.');

Datei anzeigen

@ -1,20 +1,27 @@
package com.comphenix.protocol.wrappers.nbt; package com.comphenix.protocol.wrappers.nbt;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import net.minecraft.server.v1_7_R3.NBTTagCompound; import net.minecraft.server.v1_7_R3.NBTTagCompound;
import net.minecraft.server.v1_7_R3.TileEntityChest;
import net.sf.cglib.asm.ClassReader; import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor; import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes; import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor;
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.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -37,6 +44,10 @@ class TileEntityAccessor<T extends BlockState> {
private MethodAccessor readCompound; private MethodAccessor readCompound;
private MethodAccessor writeCompound; private MethodAccessor writeCompound;
// For CGLib detection
private boolean writeDetected;
private boolean readDetected;
private TileEntityAccessor() { private TileEntityAccessor() {
// Do nothing // Do nothing
} }
@ -45,18 +56,31 @@ class TileEntityAccessor<T extends BlockState> {
* Construct a new tile entity accessor. * Construct a new tile entity accessor.
* @param tileEntityField - the tile entity field. * @param tileEntityField - the tile entity field.
* @param tileEntity - the tile entity. * @param tileEntity - the tile entity.
* @param tile - the block state.
* @throws IOException Cannot read tile entity. * @throws IOException Cannot read tile entity.
*/ */
private TileEntityAccessor(FieldAccessor tileEntityField) { private TileEntityAccessor(FieldAccessor tileEntityField, T state) {
if (tileEntityField != null) { if (tileEntityField != null) {
this.tileEntityField = tileEntityField; this.tileEntityField = tileEntityField;
Class<?> type = tileEntityField.getField().getType();
// Possible read/write methods // Possible read/write methods
try { try {
findSerializationMethods(tileEntityField.getField().getType()); findMethodsUsingASM(type);
} catch (IOException e) { } catch (IOException ex1) {
throw new RuntimeException("Cannot find read/write methods.", e); try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
} }
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
} }
} }
@ -66,11 +90,11 @@ class TileEntityAccessor<T extends BlockState> {
* @param nbtCompoundClass - the compound clas. * @param nbtCompoundClass - the compound clas.
* @throws IOException If we cannot find these methods. * @throws IOException If we cannot find these methods.
*/ */
private void findSerializationMethods(final Class<?> tileEntityClass) throws IOException { private void findMethodsUsingASM(final Class<?> tileEntityClass) throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass(); final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName()); final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
final String tagCompoundName = getJarName(NBTTagCompound.class);
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)V"; final String expectedDesc = "(L" + tagCompoundName + ";)V";
reader.accept(new EmptyClassVisitor() { reader.accept(new EmptyClassVisitor() {
@ -113,12 +137,51 @@ class TileEntityAccessor<T extends BlockState> {
return null; return null;
} }
}, 0); }, 0);
}
/**
* Find the read/write methods in TileEntity.
* @param blockState - the block state.
* @throws IOException If we cannot find these methods.
*/
private void findMethodUsingCGLib(T blockState) throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
// Ensure we found them // This is a much slower method, but it is necessary in MCPC
if (readCompound == null) Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
throw new RuntimeException("Unable to find read method in " + tileEntityClass); enhancer.setSuperclass(nbtCompoundClass);
if (writeCompound == null) enhancer.setCallback(new MethodInterceptor() {
throw new RuntimeException("Unable to find write method in " + tileEntityClass); @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getReturnType().equals(Void.TYPE)) {
// Write method
writeDetected = true;
} else {
// Read method
readDetected = true;
}
throw new RuntimeException("Stop execution.");
}
});
Object compound = enhancer.create();
Object tileEntity = tileEntityField.get(blockState);
// Look in every read/write like method
for (Method method : FuzzyReflection.fromObject(tileEntity, true).
getMethodListByParameters(Void.TYPE, new Class<?>[] { nbtCompoundClass })) {
try {
readDetected = false;
writeDetected = false;
method.invoke(tileEntity, compound);
} catch (Exception e) {
// Okay - see if we detected a write or read
if (readDetected)
readCompound = Accessors.getMethodAccessor(method, true);
if (writeDetected)
writeCompound = Accessors.getMethodAccessor(method, true);
}
}
} }
/** /**
@ -177,7 +240,7 @@ class TileEntityAccessor<T extends BlockState> {
created = EMPTY_ACCESSOR; created = EMPTY_ACCESSOR;
} }
if (field != null) { if (field != null) {
created = new TileEntityAccessor<T>(field); created = new TileEntityAccessor<T>(field, state);
} }
accessor = cachedAccessors.putIfAbsent(craftBlockState, created); accessor = cachedAccessors.putIfAbsent(craftBlockState, created);

Datei anzeigen

@ -11,6 +11,7 @@ import net.minecraft.server.v1_7_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_7_R3.ServerPing; import net.minecraft.server.v1_7_R3.ServerPing;
import net.minecraft.server.v1_7_R3.ServerPingPlayerSample; import net.minecraft.server.v1_7_R3.ServerPingPlayerSample;
import net.minecraft.server.v1_7_R3.ServerPingServerData; import net.minecraft.server.v1_7_R3.ServerPingServerData;
import net.minecraft.server.v1_7_R3.WatchableObject;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
@ -104,4 +105,9 @@ public class MinecraftReflectionTest {
public void testChunkCoordIntPair() { public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair()); assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
} }
@Test
public void testWatchableObject() {
assertEquals(WatchableObject.class, MinecraftReflection.getWatchableObjectClass());
}
} }