From 18ef06ea21b2399fc61b1e55853a1a72ebfbc48a Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Fri, 5 Oct 2012 01:41:17 +0200 Subject: [PATCH] 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. --- ProtocolLib/.classpath | 1 - .../injector/PacketFilterManager.java | 8 +-- .../player/NetworkServerInjector.java | 20 ++++++- .../injector/player/NetworkSpoutHook.java | 60 ------------------- .../player/PlayerInjectionHandler.java | 5 ++ .../injector/player/PlayerInjector.java | 15 ++++- .../protocol/reflect/ObjectCloner.java | 8 +++ .../reflect/instances/DefaultInstances.java | 4 +- .../reflect/instances/ExistingGenerator.java | 28 ++++++++- 9 files changed, 78 insertions(+), 71 deletions(-) delete mode 100644 ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkSpoutHook.java diff --git a/ProtocolLib/.classpath b/ProtocolLib/.classpath index 408271fe..0bc481ca 100644 --- a/ProtocolLib/.classpath +++ b/ProtocolLib/.classpath @@ -10,6 +10,5 @@ - diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java index 3d4c4c0e..7a1c612d 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java @@ -441,17 +441,17 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok try { manager.registerEvents(new Listener() { - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerJoin(PlayerJoinEvent event) { playerInjection.injectPlayer(event.getPlayer()); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerQuit(PlayerQuitEvent event) { playerInjection.uninjectPlayer(event.getPlayer()); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPluginDisabled(PluginDisableEvent event) { // Clean up in case the plugin forgets if (event.getPlugin() != plugin) { @@ -487,7 +487,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok Class eventPriority = loader.loadClass("org.bukkit.event.Event$Priority"); // Get the priority - Object priorityNormal = Enum.valueOf(eventPriority, "Normal"); + Object priorityNormal = Enum.valueOf(eventPriority, "Highest"); // Get event types Object playerJoinType = Enum.valueOf(eventTypes, "PLAYER_JOIN"); diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 243c0fc7..55ebd698 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -136,8 +136,17 @@ public class NetworkServerInjector extends PlayerInjector { }); // 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)); + } + serverInstances.setNonNull(true); serverInstances.setMaximumRecursion(1); @@ -154,6 +163,15 @@ public class NetworkServerInjector extends PlayerInjector { "Cannot hook player: Unable to find a valid constructor for the NetServerHandler object."); } } + + 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 public void cleanupAll() { diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkSpoutHook.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkSpoutHook.java deleted file mode 100644 index e71bacef..00000000 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkSpoutHook.java +++ /dev/null @@ -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 - } -} diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java index 1ceb0725..d10bfb64 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java @@ -23,6 +23,11 @@ import com.comphenix.protocol.injector.PlayerLoggedOutException; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.google.common.collect.ImmutableSet; +/** + * Responsible for injecting into a player's sendPacket method. + * + * @author Kristian + */ public class PlayerInjectionHandler { // Server connection injection diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java index 9a1779ec..f80b8e49 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -50,6 +50,9 @@ abstract class PlayerInjector { protected static Field inputField; protected static Field netHandlerField; + // Whether or not we're using a proxy type + private static boolean hasProxyType; + // To add our injected array lists protected static StructureModifier 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) { try { @@ -154,6 +165,8 @@ abstract class PlayerInjector { if (handler instanceof Factory) return null; + hasProxyType = true; + // No? Is it a Proxy type? try { FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true); @@ -162,7 +175,7 @@ abstract class PlayerInjector { return reflection.getFieldByType(".*NetServerHandler"); } 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!"); } } diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/ObjectCloner.java b/ProtocolLib/src/com/comphenix/protocol/reflect/ObjectCloner.java index 65902cbd..e64c32a8 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/ObjectCloner.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/ObjectCloner.java @@ -55,6 +55,14 @@ public class ObjectCloner { // System.out.println(String.format("Writing value %s to %s", // 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) { throw new RuntimeException("Unable to copy fields from " + commonType.getName(), e); } diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java index 8c3b8ebc..fad95379 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java @@ -235,8 +235,9 @@ public class DefaultInstances { params[i] = getDefaultInternal(types[i], providers, recursionLevel + 1); // Did we break the non-null contract? - if (params[i] == null && nonNull) + if (params[i] == null && nonNull) { return null; + } } return createInstance(type, minimum, types, params); @@ -244,7 +245,6 @@ public class DefaultInstances { } catch (Exception e) { // Nope, we couldn't create this type - e.printStackTrace(); } // No suitable default value could be found diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java index 97bad0a6..25f78a03 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java @@ -33,13 +33,37 @@ public class ExistingGenerator implements InstanceProvider { * @return The instance generator. */ 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. + *

+ * 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(); + // 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. - for (Field field : FuzzyReflection.fromObject(object, true).getFields()) { + for (Field field : FuzzyReflection.fromClass(type, true).getFields()) { try { Object value = FieldUtils.readField(field, object, true); - + // Use the type of the field, not the object itself if (value != null) generator.addObject(field.getType(), value);