Added two different player injection hooks, for compatibility with

TagAPI.
This commit is contained in:
Kristian S. Stangeland 2012-09-15 13:47:14 +02:00
parent 616213924b
commit aa2dcefa0d
4 changed files with 269 additions and 119 deletions

View File

@ -10,7 +10,7 @@ import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; 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. * The array list that notifies when packets are sent by the server.

View File

@ -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<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>());
// Overridden fields
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
// Sync field
private static Field syncField;
private Object syncObject;
public NetworkFieldInjector(Player player, PacketFilterManager manager, Set<Integer> 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> list = networkModifier.withType(List.class);
// Subclass both send queues
for (Field field : list.getFields()) {
VolatileField overwriter = new VolatileField(field, networkManager, true);
@SuppressWarnings("unchecked")
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
synchronized(syncObject) {
// The list we'll be inserting
List<Packet> 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<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
List<Packet> hacketList = (List<Packet>) 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<Packet>) overriden.getValue()) {
minecraftList.add(packet);
}
} finally {
overriden.revertValue();
}
}
}
overridenLists.clear();
}
}

View File

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

View File

@ -21,11 +21,7 @@ import java.io.DataInputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.server.EntityPlayer; import net.minecraft.server.EntityPlayer;
import net.minecraft.server.Packet; 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.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.google.common.collect.Sets;
class PlayerInjector { abstract class PlayerInjector {
/**
* Marker interface that indicates a packet is fake and should not be processed.
* @author Kristian
*/
public interface FakePacket {
// Nothing
}
/** /**
* Sets the inject hook type. Different types allow for maximum compatibility. * Sets the inject hook type. Different types allow for maximum compatibility.
@ -68,40 +55,34 @@ class PlayerInjector {
} }
// Cache previously retrieved fields // Cache previously retrieved fields
private static Field serverHandlerField; protected static Field serverHandlerField;
private static Field networkManagerField; protected static Field networkManagerField;
private static Field inputField; protected static Field inputField;
private static Field netHandlerField; protected static Field netHandlerField;
// To add our injected array lists // To add our injected array lists
private static StructureModifier<Object> networkModifier; protected static StructureModifier<Object> networkModifier;
// And methods // And methods
private static Method queueMethod; protected static Method queueMethod;
private static Method processMethod; protected static Method processMethod;
private Player player; protected Player player;
private boolean hasInitialized; protected boolean hasInitialized;
// Reference to the player's network manager // Reference to the player's network manager
private VolatileField networkManagerRef; protected VolatileField networkManagerRef;
private Object networkManager; protected Object networkManager;
// Current net handler // Current net handler
private Object netHandler; protected Object netHandler;
// Overridden fields
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
// Packets to ignore
private Set<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>());
// The packet manager and filters // The packet manager and filters
private PacketFilterManager manager; protected PacketFilterManager manager;
private Set<Integer> sendingFilters; protected Set<Integer> sendingFilters;
// Previous data input // Previous data input
private DataInputStream cachedInput; protected DataInputStream cachedInput;
public PlayerInjector(Player player, PacketFilterManager manager, Set<Integer> sendingFilters) throws IllegalAccessException { public PlayerInjector(Player player, PacketFilterManager manager, Set<Integer> sendingFilters) throws IllegalAccessException {
this.player = player; this.player = player;
@ -110,7 +91,7 @@ class PlayerInjector {
initialize(); initialize();
} }
private void initialize() throws IllegalAccessException { protected void initialize() throws IllegalAccessException {
CraftPlayer craft = (CraftPlayer) player; CraftPlayer craft = (CraftPlayer) player;
EntityPlayer notchEntity = craft.getHandle(); EntityPlayer notchEntity = craft.getHandle();
@ -214,63 +195,17 @@ class PlayerInjector {
* @param filtered - whether or not the packet will be filtered by our listeners. * @param filtered - whether or not the packet will be filtered by our listeners.
* @param InvocationTargetException If an error occured when sending the packet. * @param InvocationTargetException If an error occured when sending the packet.
*/ */
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException { public abstract 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 void injectManager() { /**
* Inject a hook to catch packets sent to the current player.
if (networkManager != null) { */
public abstract void injectManager();
@SuppressWarnings("rawtypes")
StructureModifier<List> list = networkModifier.withType(List.class); /**
* Remove all hooks and modifications.
// Subclass both send queues */
for (Field field : list.getFields()) { public abstract void cleanupAll();
VolatileField overwriter = new VolatileField(field, networkManager, true);
@SuppressWarnings("unchecked")
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
synchronized(minecraftList) {
// The list we'll be inserting
List<Packet> 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);
}
}
}
/** /**
* Allows a packet to be recieved by the listeners. * Allows a packet to be recieved by the listeners.
@ -299,6 +234,11 @@ class PlayerInjector {
return packet; 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) { public DataInputStream getInputStream(boolean cache) {
// Get the associated input stream // Get the associated input stream
try { try {
@ -313,30 +253,4 @@ class PlayerInjector {
throw new RuntimeException("Unable to read input stream.", e); throw new RuntimeException("Unable to read input stream.", e);
} }
} }
@SuppressWarnings("unchecked")
public void cleanupAll() {
// Clean up
for (VolatileField overriden : overridenLists) {
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
List<Packet> hacketList = (List<Packet>) 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<Packet>) overriden.getValue()) {
minecraftList.add(packet);
}
} finally {
overriden.revertValue();
}
}
}
overridenLists.clear();
}
} }