Added yet another player hook method.

This method works by injecting into the NetServerHandler object
of a player. That way, we can also intercept map chunk packets.
This commit is contained in:
Kristian S. Stangeland 2012-09-26 02:26:44 +02:00
parent 9e402a3ab4
commit e04a78fc04
3 changed files with 143 additions and 8 deletions

View File

@ -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<Integer> 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;
}
}

View File

@ -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.
* <p>
* Cannot intercept MapChunk packets.
*/
NETWORK_HANDLER_FIELDS,
/**
* Override the network handler object itself.
* Override the network handler object itself. Only works in 1.3.
* <p>
* 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<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
// 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.");
}

View File

@ -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)