mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-27 21:26:17 +01:00
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.
This commit is contained in:
parent
71ce362c8e
commit
8a2e696363
@ -25,6 +25,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.HashSet;
|
||||
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_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.
|
||||
* <p>
|
||||
* Replaced by the method {@link #getMinecraftObjectRegex()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+";
|
||||
public static final String MINECRAFT_OBJECT = "net\\.minecraft\\." + CANONICAL_REGEX;
|
||||
|
||||
/**
|
||||
* Regular expression computed dynamically.
|
||||
@ -213,6 +219,15 @@ public class MinecraftReflection {
|
||||
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
|
||||
if (packageMatcher.matches()) {
|
||||
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
|
||||
@ -241,7 +256,7 @@ public class MinecraftReflection {
|
||||
// The package is usualy flat, so go with that assumption
|
||||
String matcher =
|
||||
(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
|
||||
setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_OBJECT + ")");
|
||||
@ -266,7 +281,7 @@ public class MinecraftReflection {
|
||||
throw new IllegalStateException("Could not find Bukkit. Is it running?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the Minecraft library package string.
|
||||
* @return The library package.
|
||||
@ -1249,11 +1264,11 @@ public class MinecraftReflection {
|
||||
public static Class<?> getWatchableObjectClass() {
|
||||
try {
|
||||
return getMinecraftClass("WatchableObject");
|
||||
} catch (RuntimeException e) {
|
||||
} catch (RuntimeException e) {
|
||||
Method selected = FuzzyReflection.fromClass(getDataWatcherClass(), true).
|
||||
getMethod(FuzzyMethodContract.newBuilder().
|
||||
requireModifier(Modifier.STATIC).
|
||||
parameterDerivedOf(DataOutput.class, 0).
|
||||
parameterDerivedOf(isUsingNetty() ? getPacketDataSerializerClass() : DataOutput.class, 0).
|
||||
parameterMatches(getMinecraftObjectMatcher(), 1).
|
||||
build());
|
||||
|
||||
@ -1270,19 +1285,37 @@ public class MinecraftReflection {
|
||||
try {
|
||||
return getMinecraftClass("ServerConnection");
|
||||
} catch (RuntimeException e) {
|
||||
FuzzyClassContract serverConnectionContract = FuzzyClassContract.newBuilder().
|
||||
Method selected = null;
|
||||
FuzzyClassContract.Builder serverConnectionContract = FuzzyClassContract.newBuilder().
|
||||
constructor(FuzzyMethodContract.newBuilder().
|
||||
parameterExactType(getMinecraftServerClass()).
|
||||
parameterCount(1)).
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
parameterExactType(getNetServerHandlerClass())).
|
||||
build();
|
||||
parameterCount(1));
|
||||
|
||||
Method selected = FuzzyReflection.fromClass(getMinecraftServerClass()).
|
||||
getMethod(FuzzyMethodContract.newBuilder().
|
||||
requireModifier(Modifier.ABSTRACT).
|
||||
returnTypeMatches(serverConnectionContract).
|
||||
build());
|
||||
if (isUsingNetty()) {
|
||||
serverConnectionContract.
|
||||
method(FuzzyMethodContract.newBuilder().
|
||||
parameterDerivedOf(InetAddress.class, 0).
|
||||
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
|
||||
return setMinecraftClass("ServerConnection", selected.getReturnType());
|
||||
|
@ -98,7 +98,7 @@ class RemappedClassSource extends ClassSource {
|
||||
* @param path - the canonical class name.
|
||||
* @return The obfuscated class name.
|
||||
*/
|
||||
private String getClassName(String path) {
|
||||
public String getClassName(String path) {
|
||||
try {
|
||||
String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/'));
|
||||
return remapped.replace('/', '.');
|
||||
|
@ -1,20 +1,27 @@
|
||||
package com.comphenix.protocol.wrappers.nbt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
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.MethodVisitor;
|
||||
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 com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -37,6 +44,10 @@ class TileEntityAccessor<T extends BlockState> {
|
||||
private MethodAccessor readCompound;
|
||||
private MethodAccessor writeCompound;
|
||||
|
||||
// For CGLib detection
|
||||
private boolean writeDetected;
|
||||
private boolean readDetected;
|
||||
|
||||
private TileEntityAccessor() {
|
||||
// Do nothing
|
||||
}
|
||||
@ -45,18 +56,31 @@ class TileEntityAccessor<T extends BlockState> {
|
||||
* Construct a new tile entity accessor.
|
||||
* @param tileEntityField - the tile entity field.
|
||||
* @param tileEntity - the tile entity.
|
||||
* @param tile - the block state.
|
||||
* @throws IOException Cannot read tile entity.
|
||||
*/
|
||||
private TileEntityAccessor(FieldAccessor tileEntityField) {
|
||||
private TileEntityAccessor(FieldAccessor tileEntityField, T state) {
|
||||
if (tileEntityField != null) {
|
||||
this.tileEntityField = tileEntityField;
|
||||
Class<?> type = tileEntityField.getField().getType();
|
||||
|
||||
// Possible read/write methods
|
||||
try {
|
||||
findSerializationMethods(tileEntityField.getField().getType());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot find read/write methods.", e);
|
||||
findMethodsUsingASM(type);
|
||||
} catch (IOException ex1) {
|
||||
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.
|
||||
* @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 ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
|
||||
final String tagCompoundName = getJarName(NBTTagCompound.class);
|
||||
|
||||
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
|
||||
final String expectedDesc = "(L" + tagCompoundName + ";)V";
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
@ -113,12 +137,51 @@ class TileEntityAccessor<T extends BlockState> {
|
||||
return null;
|
||||
}
|
||||
}, 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
|
||||
if (readCompound == null)
|
||||
throw new RuntimeException("Unable to find read method in " + tileEntityClass);
|
||||
if (writeCompound == null)
|
||||
throw new RuntimeException("Unable to find write method in " + tileEntityClass);
|
||||
// This is a much slower method, but it is necessary in MCPC
|
||||
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
|
||||
enhancer.setSuperclass(nbtCompoundClass);
|
||||
enhancer.setCallback(new MethodInterceptor() {
|
||||
@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;
|
||||
}
|
||||
if (field != null) {
|
||||
created = new TileEntityAccessor<T>(field);
|
||||
created = new TileEntityAccessor<T>(field, state);
|
||||
}
|
||||
accessor = cachedAccessors.putIfAbsent(craftBlockState, created);
|
||||
|
||||
|
@ -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.ServerPingPlayerSample;
|
||||
import net.minecraft.server.v1_7_R3.ServerPingServerData;
|
||||
import net.minecraft.server.v1_7_R3.WatchableObject;
|
||||
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
@ -104,4 +105,9 @@ public class MinecraftReflectionTest {
|
||||
public void testChunkCoordIntPair() {
|
||||
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWatchableObject() {
|
||||
assertEquals(WatchableObject.class, MinecraftReflection.getWatchableObjectClass());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user