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:
Ursprung
71ce362c8e
Commit
8a2e696363
@ -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());
|
||||||
|
@ -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('/', '.');
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren