Make the proxy creation even more flexible.

Now we even support Orebfuscator without using Spout.
This commit is contained in:
Kristian S. Stangeland 2012-10-05 03:12:35 +02:00
parent 18ef06ea21
commit 0e76d8ea2b
4 changed files with 98 additions and 33 deletions

View File

@ -21,6 +21,7 @@ import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
@ -97,6 +98,29 @@ public class NetworkServerInjector extends PlayerInjector {
if (serverHandlerRef.getValue() instanceof Factory)
return;
if (!tryInjectManager()) {
// Try to override the proxied object
if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue();
if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
// Try again
if (tryInjectManager()) {
// It worked - probably
return;
}
}
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
}
}
private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass();
Enhancer ex = new Enhancer();
@ -135,18 +159,22 @@ public class NetworkServerInjector extends PlayerInjector {
}
});
// Use the existing field values when we create our copy
// Find the Minecraft NetServerHandler superclass
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
DefaultInstances serverInstances = null;
if (hasProxyServerHandler()) {
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
serverInstances = DefaultInstances.fromArray(
ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass));
// Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) {
serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
} else {
serverInstances = DefaultInstances.fromArray(
ExistingGenerator.fromObjectFields(serverHandler));
serverInstances = DefaultInstances.fromArray(generator);
}
serverInstances.setNonNull(true);
serverInstances.setMaximumRecursion(1);
@ -158,19 +186,31 @@ public class NetworkServerInjector extends PlayerInjector {
//copyTo(serverHandler, proxyObject);
serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverHandlerRef.setValue(proxyObject);
return true;
} else {
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
return false;
}
}
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
} catch (Throwable e) {
// Oh well
}
}
return null;
}
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft"))
if (clazz.getName().startsWith("net.minecraft.server."))
return clazz;
else if (clazz.equals(Object.class))
return clazz;
else
return clazz.getSuperclass();
return getFirstMinecraftSuperClass(clazz.getSuperclass());
}
@Override

View File

@ -43,8 +43,8 @@ import com.comphenix.protocol.reflect.VolatileField;
abstract class PlayerInjector {
// Cache previously retrieved fields
private static Field serverHandlerField;
private static Field proxyServerField;
protected static Field serverHandlerField;
protected static Field proxyServerField;
protected static Field networkManagerField;
protected static Field inputField;
@ -115,12 +115,7 @@ abstract class PlayerInjector {
}
// Yo dawg
if (proxyServerField != null) {
Object container = FieldUtils.readField(serverHandlerField, notchEntity, true);
serverHandlerRef = new VolatileField(proxyServerField, container);
} else {
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
}
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
serverHandler = serverHandlerRef.getValue();
// Next, get the network manager
@ -166,6 +161,7 @@ abstract class PlayerInjector {
return null;
hasProxyType = true;
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
// No? Is it a Proxy type?
try {
@ -175,7 +171,7 @@ abstract class PlayerInjector {
return reflection.getFieldByType(".*NetServerHandler");
} catch (RuntimeException e) {
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
// Damn
}
}

View File

@ -156,9 +156,12 @@ public class DefaultInstances {
* @param type - type to construct.
* @return A constructor with the fewest number of parameters, or NULL if the type has no constructors.
*/
@SuppressWarnings("unchecked")
public <T> Constructor<T> getMinimumConstructor(Class<T> type) {
return getMinimumConstructor(type, registered, 0);
}
@SuppressWarnings("unchecked")
private <T> Constructor<T> getMinimumConstructor(Class<T> type, List<InstanceProvider> providers, int recursionLevel) {
Constructor<T> minimum = null;
int lastCount = Integer.MAX_VALUE;
@ -170,6 +173,13 @@ public class DefaultInstances {
// require itself in the constructor.
if (types.length < lastCount) {
if (!contains(types, type)) {
if (nonNull) {
// Make sure all of these types are non-null
if (isAnyNull(types, providers, recursionLevel)) {
continue;
}
}
minimum = (Constructor<T>) candidate;
lastCount = types.length;
@ -183,6 +193,27 @@ public class DefaultInstances {
return minimum;
}
/**
* Determine if any of the given types will be NULL once created.
* <p>
* Recursion level is the number of times the default method has been called.
* @param types - types to check.
* @param providers - instance providers.
* @param recursionLevel - current recursion level.
* @return
*/
private boolean isAnyNull(Class<?>[] types, List<InstanceProvider> providers, int recursionLevel) {
// Just check if any of them are NULL
for (Class<?> type : types) {
if (getDefaultInternal(type, providers, recursionLevel) == null) {
System.out.println(type.getName() + " is NULL!");
return true;
}
}
return false;
}
/**
* Retrieves a default instance or value that is assignable to this type.
* <p>
@ -198,7 +229,7 @@ public class DefaultInstances {
* </ul>
* </ul>
* @param type - the type to construct a default value.
* @param providers - instance providers used during the
* @param providers - instance providers used during the construction.
* @return A default value/instance, or NULL if not possible.
*/
public <T> T getDefault(Class<T> type, List<InstanceProvider> providers) {
@ -221,7 +252,7 @@ public class DefaultInstances {
return null;
}
Constructor<T> minimum = getMinimumConstructor(type);
Constructor<T> minimum = getMinimumConstructor(type, providers, recursionLevel + 1);
// Create the type with this constructor using default values. This might fail, though.
try {

View File

@ -17,8 +17,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
*/
public class ExistingGenerator implements InstanceProvider {
@SuppressWarnings("rawtypes")
private Map<Class, Object> existingValues = new HashMap<Class, Object>();
private Map<String, Object> existingValues = new HashMap<String, Object>();
private ExistingGenerator() {
// Only accessible to the constructors
@ -72,7 +71,7 @@ public class ExistingGenerator implements InstanceProvider {
// Yes, swallow it. No, really.
}
}
return generator;
}
@ -94,19 +93,18 @@ public class ExistingGenerator implements InstanceProvider {
if (value == null)
throw new IllegalArgumentException("Value cannot be NULL.");
existingValues.put(value.getClass(), value);
existingValues.put(value.getClass().getName(), value);
}
private void addObject(Class<?> type, Object value) {
existingValues.put(type, value);
existingValues.put(type.getName(), value);
}
@Override
public Object create(@Nullable Class<?> type) {
Object value = existingValues.get(type);
Object value = existingValues.get(type.getName());
// NULL values indicate that the generator failed
return value;
}