Archiviert
13
0

Adding "support" for Spout by proxying it's NetServerHandler.

While it may seem better to use a Spout PacketListener, we can't
prevent other spout listeners from stopping the listener
chain. For instance, Orebfuscator does supports Spout, but does this
by cancelling every chunk packet and sending them to be processed and
sent by Orebfuscator only. This can never be made compatible with
other plugins.

So, we choose to add some overhead and inject our proxy onto Spout. 
Fortunately, Spout injects the proxy using a player listener on LOWEST, 
so we get to override Spout again with our HIGHEST. The proxy method
should be generic enough to handle most proxy types.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-10-05 01:41:17 +02:00
Ursprung af3e278e06
Commit 18ef06ea21
9 geänderte Dateien mit 78 neuen und 71 gelöschten Zeilen

Datei anzeigen

@ -10,6 +10,5 @@
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre6"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre6"/>
<classpathentry kind="lib" path="D:/Games/Minecraft/Server Mods/1.3.2/SpoutPlugin.jar"/>
<classpathentry kind="output" path="class"/> <classpathentry kind="output" path="class"/>
</classpath> </classpath>

Datei anzeigen

@ -441,17 +441,17 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
try { try {
manager.registerEvents(new Listener() { manager.registerEvents(new Listener() {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
playerInjection.injectPlayer(event.getPlayer()); playerInjection.injectPlayer(event.getPlayer());
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
playerInjection.uninjectPlayer(event.getPlayer()); playerInjection.uninjectPlayer(event.getPlayer());
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPluginDisabled(PluginDisableEvent event) { public void onPluginDisabled(PluginDisableEvent event) {
// Clean up in case the plugin forgets // Clean up in case the plugin forgets
if (event.getPlugin() != plugin) { if (event.getPlugin() != plugin) {
@ -487,7 +487,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority"); Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority");
// Get the priority // Get the priority
Object priorityNormal = Enum.valueOf(eventPriority, "Normal"); Object priorityNormal = Enum.valueOf(eventPriority, "Highest");
// Get event types // Get event types
Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN"); Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN");

Datei anzeigen

@ -136,8 +136,17 @@ public class NetworkServerInjector extends PlayerInjector {
}); });
// Use the existing field values when we create our copy // Use the existing field values when we create our copy
DefaultInstances serverInstances = DefaultInstances.fromArray( DefaultInstances serverInstances = null;
if (hasProxyServerHandler()) {
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
serverInstances = DefaultInstances.fromArray(
ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass));
} else {
serverInstances = DefaultInstances.fromArray(
ExistingGenerator.fromObjectFields(serverHandler)); ExistingGenerator.fromObjectFields(serverHandler));
}
serverInstances.setNonNull(true); serverInstances.setNonNull(true);
serverInstances.setMaximumRecursion(1); serverInstances.setMaximumRecursion(1);
@ -155,6 +164,15 @@ public class NetworkServerInjector extends PlayerInjector {
} }
} }
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft"))
return clazz;
else if (clazz.equals(Object.class))
return clazz;
else
return clazz.getSuperclass();
}
@Override @Override
public void cleanupAll() { public void cleanupAll() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) { if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {

Datei anzeigen

@ -1,60 +0,0 @@
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
public class NetworkSpoutHook extends PlayerInjector {
public NetworkSpoutHook(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException {
super(logger, player, invoker);
}
@Override
protected boolean hasListener(int packetID) {
return false;
}
@Override
public boolean canInject() {
return getSpout() != null;
}
private Plugin getSpout() {
// Spout must be loaded
try {
return Bukkit.getServer().getPluginManager().getPlugin("Spout");
} catch (Throwable e) {
return null;
}
}
@Override
public void injectManager() {
}
@Override
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
}
@Override
public void cleanupAll() {
// TODO Auto-generated method stub
}
@Override
public void checkListener(PacketListener listener) {
// We support everything Spout does
}
}

Datei anzeigen

@ -23,6 +23,11 @@ import com.comphenix.protocol.injector.PlayerLoggedOutException;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
/**
* Responsible for injecting into a player's sendPacket method.
*
* @author Kristian
*/
public class PlayerInjectionHandler { public class PlayerInjectionHandler {
// Server connection injection // Server connection injection

Datei anzeigen

@ -50,6 +50,9 @@ abstract class PlayerInjector {
protected static Field inputField; protected static Field inputField;
protected static Field netHandlerField; protected static Field netHandlerField;
// Whether or not we're using a proxy type
private static boolean hasProxyType;
// To add our injected array lists // To add our injected array lists
protected static StructureModifier<Object> networkModifier; protected static StructureModifier<Object> networkModifier;
@ -142,6 +145,14 @@ abstract class PlayerInjector {
} }
} }
/**
* Retrieve whether or not the server handler is a proxy object.
* @return TRUE if it is, FALSE otherwise.
*/
protected boolean hasProxyServerHandler() {
return hasProxyType;
}
private Field getProxyField(EntityPlayer notchEntity, Field serverField) { private Field getProxyField(EntityPlayer notchEntity, Field serverField) {
try { try {
@ -154,6 +165,8 @@ abstract class PlayerInjector {
if (handler instanceof Factory) if (handler instanceof Factory)
return null; return null;
hasProxyType = true;
// No? Is it a Proxy type? // No? Is it a Proxy type?
try { try {
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true); FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
@ -162,7 +175,7 @@ abstract class PlayerInjector {
return reflection.getFieldByType(".*NetServerHandler"); return reflection.getFieldByType(".*NetServerHandler");
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.log(Level.WARNING, "Server handler is a proxy type.", e); logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
} }
} }

Datei anzeigen

@ -55,6 +55,14 @@ public class ObjectCloner {
// System.out.println(String.format("Writing value %s to %s", // System.out.println(String.format("Writing value %s to %s",
// value, modifier.getFields().get(i).getName())); // value, modifier.getFields().get(i).getName()));
} }
// Copy private fields underneath
Class<?> superclass = commonType.getSuperclass();
if (!superclass.equals(Object.class)) {
copyTo(source, destination, superclass);
}
} catch (FieldAccessException e) { } catch (FieldAccessException e) {
throw new RuntimeException("Unable to copy fields from " + commonType.getName(), e); throw new RuntimeException("Unable to copy fields from " + commonType.getName(), e);
} }

Datei anzeigen

@ -235,8 +235,9 @@ public class DefaultInstances {
params[i] = getDefaultInternal(types[i], providers, recursionLevel + 1); params[i] = getDefaultInternal(types[i], providers, recursionLevel + 1);
// Did we break the non-null contract? // Did we break the non-null contract?
if (params[i] == null && nonNull) if (params[i] == null && nonNull) {
return null; return null;
}
} }
return createInstance(type, minimum, types, params); return createInstance(type, minimum, types, params);
@ -244,7 +245,6 @@ public class DefaultInstances {
} catch (Exception e) { } catch (Exception e) {
// Nope, we couldn't create this type // Nope, we couldn't create this type
e.printStackTrace();
} }
// No suitable default value could be found // No suitable default value could be found

Datei anzeigen

@ -33,10 +33,34 @@ public class ExistingGenerator implements InstanceProvider {
* @return The instance generator. * @return The instance generator.
*/ */
public static ExistingGenerator fromObjectFields(Object object) { public static ExistingGenerator fromObjectFields(Object object) {
if (object == null)
throw new IllegalArgumentException("Object cannot be NULL.");
return fromObjectFields(object, object.getClass());
}
/**
* Automatically create an instance provider from a objects public and private fields.
* <p>
* If two or more fields share the same type, the last declared non-null field will take
* precedent.
* @param object - object to create an instance generator from.
* @param type - the type to cast the object.
* @return The instance generator.
*/
public static ExistingGenerator fromObjectFields(Object object, Class<?> type) {
ExistingGenerator generator = new ExistingGenerator(); ExistingGenerator generator = new ExistingGenerator();
// Possible errors
if (object == null)
throw new IllegalArgumentException("Object cannot be NULL.");
if (type == null)
throw new IllegalArgumentException("Type cannot be NULL.");
if (!type.isAssignableFrom(object.getClass()))
throw new IllegalArgumentException("Type must be a superclass or be the same type.");
// Read instances from every field. // Read instances from every field.
for (Field field : FuzzyReflection.fromObject(object, true).getFields()) { for (Field field : FuzzyReflection.fromClass(type, true).getFields()) {
try { try {
Object value = FieldUtils.readField(field, object, true); Object value = FieldUtils.readField(field, object, true);