From aa2dcefa0d28dc3f7b7af4c29d63ac8c431fcb81 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sat, 15 Sep 2012 13:47:14 +0200 Subject: [PATCH] Added two different player injection hooks, for compatibility with TagAPI. --- .../protocol/injector/InjectedArrayList.java | 2 +- .../injector/NetworkFieldInjector.java | 145 +++++++++++++++++ .../injector/NetworkObjectInjector.java | 91 +++++++++++ .../protocol/injector/PlayerInjector.java | 150 ++++-------------- 4 files changed, 269 insertions(+), 119 deletions(-) create mode 100644 ProtocolLib/src/com/comphenix/protocol/injector/NetworkFieldInjector.java create mode 100644 ProtocolLib/src/com/comphenix/protocol/injector/NetworkObjectInjector.java diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/InjectedArrayList.java b/ProtocolLib/src/com/comphenix/protocol/injector/InjectedArrayList.java index 14b8e025..5273ce57 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/InjectedArrayList.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/InjectedArrayList.java @@ -10,7 +10,7 @@ import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; -import com.comphenix.protocol.injector.PlayerInjector.FakePacket; +import com.comphenix.protocol.injector.NetworkFieldInjector.FakePacket; /** * The array list that notifies when packets are sent by the server. diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/NetworkFieldInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkFieldInjector.java new file mode 100644 index 00000000..23975981 --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkFieldInjector.java @@ -0,0 +1,145 @@ +package com.comphenix.protocol.injector; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.entity.Player; + +import com.comphenix.protocol.reflect.FieldUtils; +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.reflect.VolatileField; +import com.google.common.collect.Sets; + +import net.minecraft.server.Packet; + +/** + * Injection hook that overrides the packet queue lists in NetworkHandler. + * + * @author Kristian + */ +public class NetworkFieldInjector extends PlayerInjector { + + /** + * Marker interface that indicates a packet is fake and should not be processed. + * @author Kristian + */ + public interface FakePacket { + // Nothing + } + + // Packets to ignore + private Set ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap()); + + // Overridden fields + private List overridenLists = new ArrayList(); + + // Sync field + private static Field syncField; + private Object syncObject; + + public NetworkFieldInjector(Player player, PacketFilterManager manager, Set sendingFilters) throws IllegalAccessException { + super(player, manager, sendingFilters); + } + + @Override + protected void initialize() throws IllegalAccessException { + super.initialize(); + + // Get the sync field as well + if (hasInitialized) { + if (syncField == null) + syncField = FuzzyReflection.fromObject(networkManager, true).getFieldByType("java\\.lang\\.Object"); + syncObject = FieldUtils.readField(syncField, networkManager, true); + } + } + + @Override + public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + + if (networkManager != null) { + try { + if (!filtered) { + ignoredPackets.add(packet); + } + + // Note that invocation target exception is a wrapper for a checked exception + queueMethod.invoke(networkManager, packet); + + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw e; + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to access queue method.", e); + } + } else { + throw new IllegalStateException("Unable to load network mananager. Cannot send packet."); + } + } + + @Override + public void injectManager() { + + if (networkManager != null) { + + @SuppressWarnings("rawtypes") + StructureModifier list = networkModifier.withType(List.class); + + // Subclass both send queues + for (Field field : list.getFields()) { + VolatileField overwriter = new VolatileField(field, networkManager, true); + + @SuppressWarnings("unchecked") + List minecraftList = (List) overwriter.getOldValue(); + + synchronized(syncObject) { + // The list we'll be inserting + List hackedList = new InjectedArrayList(manager.getClassLoader(), this, ignoredPackets); + + // Add every previously stored packet + for (Packet packet : minecraftList) { + hackedList.add(packet); + } + + // Don' keep stale packets around + minecraftList.clear(); + overwriter.setValue(Collections.synchronizedList(hackedList)); + } + + overridenLists.add(overwriter); + } + } + } + + @SuppressWarnings("unchecked") + public void cleanupAll() { + // Clean up + for (VolatileField overriden : overridenLists) { + List minecraftList = (List) overriden.getOldValue(); + List hacketList = (List) overriden.getValue(); + + if (minecraftList == hacketList) { + return; + } + + // Get a lock before we modify the list + synchronized(syncObject) { + try { + // Copy over current packets + for (Packet packet : (List) overriden.getValue()) { + minecraftList.add(packet); + } + } finally { + overriden.revertValue(); + } + } + } + overridenLists.clear(); + } +} diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/NetworkObjectInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkObjectInjector.java new file mode 100644 index 00000000..b5ae0a9a --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkObjectInjector.java @@ -0,0 +1,91 @@ +package com.comphenix.protocol.injector; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; +import java.lang.reflect.Method; +import java.util.Set; + +import net.minecraft.server.Packet; + +import org.bukkit.entity.Player; + +/** + * Injection method that overrides the NetworkHandler itself, and it's sendPacket-method. + * + * @author Kristian + */ +public class NetworkObjectInjector extends PlayerInjector { + public NetworkObjectInjector(Player player, PacketFilterManager manager, Set sendingFilters) throws IllegalAccessException { + super(player, manager, sendingFilters); + } + + @Override + public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue(); + + if (networkDelegate != null) { + try { + // Note that invocation target exception is a wrapper for a checked exception + queueMethod.invoke(networkDelegate, packet); + + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw e; + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to access queue method.", e); + } + } else { + throw new IllegalStateException("Unable to load network mananager. Cannot send packet."); + } + } + + @Override + public void injectManager() { + + if (networkManager != null) { + final Class networkInterface = networkManagerField.getType(); + final Object networkDelegate = networkManagerRef.getOldValue(); + + // Create our proxy object + Object networkProxy = Proxy.newProxyInstance(networkInterface.getClassLoader(), + new Class[] { networkInterface }, new InvocationHandler() { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // OH OH! The queue method! + if (method.equals(queueMethod)) { + Packet packet = (Packet) args[0]; + + if (packet != null) { + packet = handlePacketRecieved(packet); + + // A NULL packet indicate cancelling + if (packet != null) + args[0] = packet; + else + return null; + } + } + + // Delegate to our underlying class + try { + return method.invoke(networkDelegate, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + }); + + // Inject it, if we can. + networkManagerRef.setValue(networkProxy); + } + } + + @Override + public void cleanupAll() { + // Clean up + networkManagerRef.revertValue(); + } +} diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java index 003c6da2..fa5eaa01 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java @@ -21,11 +21,7 @@ import java.io.DataInputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import net.minecraft.server.EntityPlayer; import net.minecraft.server.Packet; @@ -39,17 +35,8 @@ import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.VolatileField; -import com.google.common.collect.Sets; -class PlayerInjector { - - /** - * Marker interface that indicates a packet is fake and should not be processed. - * @author Kristian - */ - public interface FakePacket { - // Nothing - } +abstract class PlayerInjector { /** * Sets the inject hook type. Different types allow for maximum compatibility. @@ -68,40 +55,34 @@ class PlayerInjector { } // Cache previously retrieved fields - private static Field serverHandlerField; - private static Field networkManagerField; - private static Field inputField; - private static Field netHandlerField; + protected static Field serverHandlerField; + protected static Field networkManagerField; + protected static Field inputField; + protected static Field netHandlerField; // To add our injected array lists - private static StructureModifier networkModifier; + protected static StructureModifier networkModifier; // And methods - private static Method queueMethod; - private static Method processMethod; + protected static Method queueMethod; + protected static Method processMethod; - private Player player; - private boolean hasInitialized; + protected Player player; + protected boolean hasInitialized; // Reference to the player's network manager - private VolatileField networkManagerRef; - private Object networkManager; + protected VolatileField networkManagerRef; + protected Object networkManager; // Current net handler - private Object netHandler; - - // Overridden fields - private List overridenLists = new ArrayList(); - - // Packets to ignore - private Set ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap()); + protected Object netHandler; // The packet manager and filters - private PacketFilterManager manager; - private Set sendingFilters; + protected PacketFilterManager manager; + protected Set sendingFilters; // Previous data input - private DataInputStream cachedInput; + protected DataInputStream cachedInput; public PlayerInjector(Player player, PacketFilterManager manager, Set sendingFilters) throws IllegalAccessException { this.player = player; @@ -110,7 +91,7 @@ class PlayerInjector { initialize(); } - private void initialize() throws IllegalAccessException { + protected void initialize() throws IllegalAccessException { CraftPlayer craft = (CraftPlayer) player; EntityPlayer notchEntity = craft.getHandle(); @@ -214,63 +195,17 @@ class PlayerInjector { * @param filtered - whether or not the packet will be filtered by our listeners. * @param InvocationTargetException If an error occured when sending the packet. */ - public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { - - - - if (networkManager != null) { - try { - if (!filtered) { - ignoredPackets.add(packet); - } - - // Note that invocation target exception is a wrapper for a checked exception - queueMethod.invoke(networkManager, packet); - - } catch (IllegalArgumentException e) { - throw e; - } catch (InvocationTargetException e) { - throw e; - } catch (IllegalAccessException e) { - throw new IllegalStateException("Unable to access queue method.", e); - } - } else { - throw new IllegalStateException("Unable to load network mananager. Cannot send packet."); - } - } + public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException; - public void injectManager() { - - if (networkManager != null) { - - @SuppressWarnings("rawtypes") - StructureModifier list = networkModifier.withType(List.class); - - // Subclass both send queues - for (Field field : list.getFields()) { - VolatileField overwriter = new VolatileField(field, networkManager, true); - - @SuppressWarnings("unchecked") - List minecraftList = (List) overwriter.getOldValue(); - - synchronized(minecraftList) { - // The list we'll be inserting - List hackedList = new InjectedArrayList(manager.getClassLoader(), this, ignoredPackets); - - // Add every previously stored packet - for (Packet packet : minecraftList) { - hackedList.add(packet); - } - - // Don' keep stale packets around - minecraftList.clear(); - overwriter.setValue(Collections.synchronizedList(hackedList)); - } - - overridenLists.add(overwriter); - } - } - } + /** + * Inject a hook to catch packets sent to the current player. + */ + public abstract void injectManager(); + + /** + * Remove all hooks and modifications. + */ + public abstract void cleanupAll(); /** * Allows a packet to be recieved by the listeners. @@ -299,6 +234,11 @@ class PlayerInjector { return packet; } + /** + * Retrieve the current player's input stream. + * @param cache - whether or not to cache the result of this method. + * @return The player's input stream. + */ public DataInputStream getInputStream(boolean cache) { // Get the associated input stream try { @@ -313,30 +253,4 @@ class PlayerInjector { throw new RuntimeException("Unable to read input stream.", e); } } - - @SuppressWarnings("unchecked") - public void cleanupAll() { - // Clean up - for (VolatileField overriden : overridenLists) { - List minecraftList = (List) overriden.getOldValue(); - List hacketList = (List) overriden.getValue(); - - if (minecraftList == hacketList) { - return; - } - - // Get a lock before we modify the list - synchronized(hacketList) { - try { - // Copy over current packets - for (Packet packet : (List) overriden.getValue()) { - minecraftList.add(packet); - } - } finally { - overriden.revertValue(); - } - } - } - overridenLists.clear(); - } }