diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java new file mode 100644 index 00000000..7569f9b8 --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java @@ -0,0 +1,115 @@ +package com.comphenix.protocol.injector; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; + +import net.minecraft.server.Packet; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.Factory; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +import org.bukkit.entity.Player; + +import com.comphenix.protocol.reflect.FuzzyReflection; + +/** + * Represents a player hook into the NetServerHandler class. + * + * @author Kristian + */ +public class NetworkServerInjector extends PlayerInjector { + + private static Method sendPacket; + + public NetworkServerInjector(Player player, PacketFilterManager manager, Set sendingFilters) throws IllegalAccessException { + super(player, manager, sendingFilters); + } + + @Override + protected void initialize() throws IllegalAccessException { + super.initialize(); + + // Get the send packet method! + if (hasInitialized) { + if (sendPacket == null) + sendPacket = FuzzyReflection.fromObject(serverHandler).getMethodByParameters("sendPacket", Packet.class); + } + } + + @Override + public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { + Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); + + if (serverDeleage != null) { + try { + // Note that invocation target exception is a wrapper for a checked exception + sendPacket.invoke(serverDeleage, packet); + + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw e; + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to access send packet method.", e); + } + } else { + throw new IllegalStateException("Unable to load server handler. Cannot send packet."); + } + } + + @Override + public void injectManager() { + if (serverHandlerRef == null) + throw new IllegalStateException("Cannot find server handler."); + // Don't inject twice + if (serverHandlerRef.getValue() instanceof Factory) + return; + + Enhancer ex = new Enhancer(); + ex.setClassLoader(manager.getClassLoader()); + ex.setSuperclass(serverHandler.getClass()); + ex.setCallback(new MethodInterceptor() { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // The send packet method! + if (method.equals(sendPacket)) { + 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(serverHandler, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + }); + + // Inject it now + serverHandlerRef.setValue(ex.create()); + } + + @Override + public void cleanupAll() { + if (serverHandlerRef != null) + serverHandlerRef.revertValue(); + } + + @Override + public boolean canInject() { + // Probably always + return true; + } +} diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java index a74b0853..9ce1a23b 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java @@ -60,14 +60,23 @@ public final class PacketFilterManager implements ProtocolManager { */ public enum PlayerInjectHooks { /** - * Override the packet queue lists in NetworkHandler. + * Override the packet queue lists in NetworkHandler. + *

+ * Cannot intercept MapChunk packets. */ NETWORK_HANDLER_FIELDS, /** - * Override the network handler object itself. + * Override the network handler object itself. Only works in 1.3. + *

+ * Cannot intercept MapChunk packets. */ NETWORK_MANAGER_OBJECT, + + /** + * Override the server handler object. Versatile, but slow. + */ + NETWORK_SERVER_OBJECT; } // Create a concurrent set @@ -79,7 +88,7 @@ public final class PacketFilterManager implements ProtocolManager { private Map playerInjection = new HashMap(); // Player injection type - private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_HANDLER_FIELDS; + private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT; // Packet injection private PacketInjector packetInjector; @@ -373,6 +382,8 @@ public final class PacketFilterManager implements ProtocolManager { return new NetworkFieldInjector(player, this, sendingFilters); case NETWORK_MANAGER_OBJECT: return new NetworkObjectInjector(player, this, sendingFilters); + case NETWORK_SERVER_OBJECT: + return new NetworkServerInjector(player, this, sendingFilters); default: throw new IllegalArgumentException("Cannot construct a player injector."); } diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java index b57baa09..5dd24cf2 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java @@ -56,9 +56,11 @@ abstract class PlayerInjector { // Reference to the player's network manager protected VolatileField networkManagerRef; + protected VolatileField serverHandlerRef; protected Object networkManager; // Current net handler + protected Object serverHandler; protected Object netHandler; // The packet manager and filters @@ -75,12 +77,18 @@ abstract class PlayerInjector { initialize(); } + /** + * Retrieve the notch (NMS) entity player object. + * @return Notch player object. + */ + protected EntityPlayer getEntityPlayer() { + CraftPlayer craft = (CraftPlayer) player; + return craft.getHandle(); + } + protected void initialize() throws IllegalAccessException { - CraftPlayer craft = (CraftPlayer) player; - EntityPlayer notchEntity = craft.getHandle(); - - Object serverHandler = null; + EntityPlayer notchEntity = getEntityPlayer(); if (!hasInitialized) { // Do this first, in case we encounter an exception @@ -89,7 +97,8 @@ abstract class PlayerInjector { // Retrieve the server handler if (serverHandlerField == null) serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler"); - serverHandler = FieldUtils.readField(serverHandlerField, notchEntity); + serverHandlerRef = new VolatileField(serverHandlerField, notchEntity); + serverHandler = serverHandlerRef.getValue(); // Next, get the network manager if (networkManagerField == null)