Archiviert
13
0

First test of the pre-login injector.

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-10-15 00:31:55 +02:00
Ursprung 39805c5502
Commit 9b4b161602
7 geänderte Dateien mit 2012 neuen und 1929 gelöschten Zeilen

Datei anzeigen

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>1.3.3</version>
<version>1.3.3-SNAPSHOT</version>
<packaging>jar</packaging>
<description>Provides read/write access to the Minecraft protocol.</description>

Datei anzeigen

@ -1,196 +1,204 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
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 java.util.logging.Logger;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
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
*/
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;
// Determine if we're listening
private Set<Integer> sendingFilters;
// Used to construct proxy objects
private ClassLoader classLoader;
public NetworkFieldInjector(ClassLoader classLoader, Logger logger, Player player,
ListenerInvoker manager, Set<Integer> sendingFilters) throws IllegalAccessException {
super(logger, player, manager);
this.classLoader = classLoader;
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public synchronized 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 checkListener(PacketListener listener) {
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
}
}
@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(classLoader, 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();
}
@Override
public boolean canInject() {
return true;
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
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 java.util.logging.Logger;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
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
*/
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;
// Determine if we're listening
private Set<Integer> sendingFilters;
// Used to construct proxy objects
private ClassLoader classLoader;
public NetworkFieldInjector(ClassLoader classLoader, Logger logger, Player player,
ListenerInvoker manager, Set<Integer> sendingFilters) throws IllegalAccessException {
super(logger, player, manager);
this.classLoader = classLoader;
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public synchronized 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 checkListener(PacketListener listener) {
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
}
}
@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(classLoader, 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();
}
@Override
public boolean canInject(GamePhase phase) {
// All phases should work
return true;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_HANDLER_FIELDS;
}
}

Datei anzeigen

@ -1,143 +1,151 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import net.minecraft.server.Packet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.logging.Logger;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
/**
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
*
* @author Kristian
*/
class NetworkObjectInjector extends PlayerInjector {
// Determine if we're listening
private Set<Integer> sendingFilters;
public NetworkObjectInjector(Logger logger, Player player, ListenerInvoker invoker, Set<Integer> sendingFilters) throws IllegalAccessException {
super(logger, player, invoker);
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@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 checkListener(PacketListener listener) {
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
}
}
@Override
public void injectManager() {
if (networkManager != null) {
final Class<?> networkInterface = networkManagerField.getType();
final Object networkDelegate = networkManagerRef.getOldValue();
if (!networkInterface.isInterface()) {
throw new UnsupportedOperationException(
"Must use CraftBukkit 1.3.0 or later to inject into into NetworkMananger.");
}
// 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();
}
@Override
public boolean canInject() {
return true;
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import net.minecraft.server.Packet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.logging.Logger;
import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
/**
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
*
* @author Kristian
*/
class NetworkObjectInjector extends PlayerInjector {
// Determine if we're listening
private Set<Integer> sendingFilters;
public NetworkObjectInjector(Logger logger, Player player, ListenerInvoker invoker, Set<Integer> sendingFilters) throws IllegalAccessException {
super(logger, player, invoker);
this.sendingFilters = sendingFilters;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@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 checkListener(PacketListener listener) {
// Unfortunately, we don't support chunk packets
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
}
}
@Override
public void injectManager() {
if (networkManager != null) {
final Class<?> networkInterface = networkManagerField.getType();
final Object networkDelegate = networkManagerRef.getOldValue();
if (!networkInterface.isInterface()) {
throw new UnsupportedOperationException(
"Must use CraftBukkit 1.3.0 or later to inject into into NetworkMananger.");
}
// 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();
}
@Override
public boolean canInject(GamePhase phase) {
// Works for all phases
return true;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_MANAGER_OBJECT;
}
}

Datei anzeigen

@ -1,266 +1,274 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
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 net.sf.cglib.proxy.NoOp;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
/**
* Represents a player hook into the NetServerHandler class.
*
* @author Kristian
*/
public class NetworkServerInjector extends PlayerInjector {
private static Method sendPacketMethod;
private InjectedServerConnection serverInjection;
// Determine if we're listening
private Set<Integer> sendingFilters;
// Used to create proxy objects
private ClassLoader classLoader;
public NetworkServerInjector(
ClassLoader classLoader, Logger logger, Player player,
ListenerInvoker invoker, Set<Integer> sendingFilters,
InjectedServerConnection serverInjection) throws IllegalAccessException {
super(logger, player, invoker);
this.classLoader = classLoader;
this.sendingFilters = sendingFilters;
this.serverInjection = serverInjection;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public void initialize() throws IllegalAccessException {
super.initialize();
// Get the send packet method!
if (hasInitialized) {
if (sendPacketMethod == null)
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
}
}
@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
sendPacketMethod.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;
if (!tryInjectManager()) {
// Try to override the proxied object
if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue();
if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
// Try again
if (tryInjectManager()) {
// It worked - probably
return;
}
}
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
}
}
private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass();
Enhancer ex = new Enhancer();
Callback sendPacketCallback = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
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;
}
// Call the method directly
return proxy.invokeSuper(obj, args);
};
};
Callback noOpCallback = NoOp.INSTANCE;
ex.setClassLoader(classLoader);
ex.setSuperclass(serverClass);
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
ex.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.equals(sendPacketMethod))
return 0;
else
return 1;
}
});
// Find the Minecraft NetServerHandler superclass
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
DefaultInstances serverInstances = null;
// Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) {
serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
} else {
serverInstances = DefaultInstances.fromArray(generator);
}
serverInstances.setNonNull(true);
serverInstances.setMaximumRecursion(1);
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
// Inject it now
if (proxyObject != null) {
// This will be done by InjectedServerConnection instead
//copyTo(serverHandler, proxyObject);
serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverHandlerRef.setValue(proxyObject);
return true;
} else {
return false;
}
}
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
} catch (Throwable e) {
// Oh well
}
}
return null;
}
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft.server."))
return clazz;
else if (clazz.equals(Object.class))
return clazz;
else
return getFirstMinecraftSuperClass(clazz.getSuperclass());
}
@Override
public void cleanupAll() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
try {
if (getNetHandler() != null) {
// Restore packet listener
try {
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
} catch (IllegalAccessException e) {
// Oh well
e.printStackTrace();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
serverInjection.revertServerHandler(serverHandler);
}
@Override
public void checkListener(PacketListener listener) {
// We support everything
}
@Override
public boolean canInject() {
return true;
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
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 net.sf.cglib.proxy.NoOp;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectCloner;
import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
/**
* Represents a player hook into the NetServerHandler class.
*
* @author Kristian
*/
public class NetworkServerInjector extends PlayerInjector {
private static Method sendPacketMethod;
private InjectedServerConnection serverInjection;
// Determine if we're listening
private Set<Integer> sendingFilters;
// Used to create proxy objects
private ClassLoader classLoader;
public NetworkServerInjector(
ClassLoader classLoader, Logger logger, Player player,
ListenerInvoker invoker, Set<Integer> sendingFilters,
InjectedServerConnection serverInjection) throws IllegalAccessException {
super(logger, player, invoker);
this.classLoader = classLoader;
this.sendingFilters = sendingFilters;
this.serverInjection = serverInjection;
}
@Override
protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID);
}
@Override
public void initialize() throws IllegalAccessException {
super.initialize();
// Get the send packet method!
if (hasInitialized) {
if (sendPacketMethod == null)
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
}
}
@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
sendPacketMethod.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;
if (!tryInjectManager()) {
// Try to override the proxied object
if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue();
if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
// Try again
if (tryInjectManager()) {
// It worked - probably
return;
}
}
throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
}
}
private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass();
Enhancer ex = new Enhancer();
Callback sendPacketCallback = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
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;
}
// Call the method directly
return proxy.invokeSuper(obj, args);
};
};
Callback noOpCallback = NoOp.INSTANCE;
ex.setClassLoader(classLoader);
ex.setSuperclass(serverClass);
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
ex.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.equals(sendPacketMethod))
return 0;
else
return 1;
}
});
// Find the Minecraft NetServerHandler superclass
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
DefaultInstances serverInstances = null;
// Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) {
serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
} else {
serverInstances = DefaultInstances.fromArray(generator);
}
serverInstances.setNonNull(true);
serverInstances.setMaximumRecursion(1);
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
// Inject it now
if (proxyObject != null) {
// This will be done by InjectedServerConnection instead
//copyTo(serverHandler, proxyObject);
serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverHandlerRef.setValue(proxyObject);
return true;
} else {
return false;
}
}
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
} catch (Throwable e) {
// Oh well
}
}
return null;
}
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
if (clazz.getName().startsWith("net.minecraft.server."))
return clazz;
else if (clazz.equals(Object.class))
return clazz;
else
return getFirstMinecraftSuperClass(clazz.getSuperclass());
}
@Override
public void cleanupAll() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
try {
if (getNetHandler() != null) {
// Restore packet listener
try {
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
} catch (IllegalAccessException e) {
// Oh well
e.printStackTrace();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
serverInjection.revertServerHandler(serverHandler);
}
@Override
public void checkListener(PacketListener listener) {
// We support everything
}
@Override
public boolean canInject(GamePhase phase) {
// Doesn't work when logging in
return phase == GamePhase.PLAYING || phase == GamePhase.CLOSING;
}
@Override
public PlayerInjectHooks getHookType() {
return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
}
}

Datei anzeigen

@ -1,327 +1,362 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.io.DataInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
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
private InjectedServerConnection serverInjection;
// The last successful player hook
private PlayerInjector lastSuccessfulHook;
// Player injection
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
// Player injection type
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
// Error logger
private Logger logger;
// Whether or not we're closing
private boolean hasClosed;
// Used to invoke events
private ListenerInvoker invoker;
// Enabled packet filters
private Set<Integer> sendingFilters = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
// The class loader we're using
private ClassLoader classLoader;
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
this.classLoader = classLoader;
this.logger = logger;
this.invoker = invoker;
this.serverInjection = new InjectedServerConnection(logger, server);
}
/**
* Retrieves how the server packets are read.
* @return Injection method for reading server packets.
*/
public PlayerInjectHooks getPlayerHook() {
return playerHook;
}
/**
* Add an underlying packet handler of the given ID.
* @param packetID - packet ID to register.
*/
public void addPacketHandler(int packetID) {
sendingFilters.add(packetID);
}
/**
* Remove an underlying packet handler of ths ID.
* @param packetID - packet ID to unregister.
*/
public void removePacketHandler(int packetID) {
sendingFilters.remove(packetID);
}
/**
* Sets how the server packets are read.
* @param playerHook - the new injection method for reading server packets.
*/
public void setPlayerHook(PlayerInjectHooks playerHook) {
this.playerHook = playerHook;
}
/**
* Used to construct a player hook.
* @param player - the player to hook.
* @param hook - the hook type.
* @return A new player hoook
* @throws IllegalAccessException Unable to do our reflection magic.
*/
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
// Construct the correct player hook
switch (hook) {
case NETWORK_HANDLER_FIELDS:
return new NetworkFieldInjector(classLoader, logger, player, invoker, sendingFilters);
case NETWORK_MANAGER_OBJECT:
return new NetworkObjectInjector(logger, player, invoker, sendingFilters);
case NETWORK_SERVER_OBJECT:
return new NetworkServerInjector(classLoader, logger, player, invoker, sendingFilters, serverInjection);
default:
throw new IllegalArgumentException("Cannot construct a player injector.");
}
}
public Player getPlayerByConnection(DataInputStream inputStream) {
return connectionLookup.get(inputStream);
}
/**
* Initialize a player hook, allowing us to read server packets.
* @param player - player to hook.
*/
public void injectPlayer(Player player) {
PlayerInjector injector = null;
PlayerInjectHooks currentHook = playerHook;
boolean firstPlayer = lastSuccessfulHook == null;
// Don't inject if the class has closed
if (!hasClosed && player != null && !playerInjection.containsKey(player)) {
while (true) {
try {
injector = getHookInstance(player, currentHook);
injector.initialize();
injector.injectManager();
DataInputStream inputStream = injector.getInputStream(false);
if (!player.isOnline() || inputStream == null) {
throw new PlayerLoggedOutException();
}
playerInjection.put(player, injector);
connectionLookup.put(inputStream, player);
break;
} catch (PlayerLoggedOutException e) {
throw e;
} catch (Exception e) {
// Mark this injection attempt as a failure
logger.log(Level.SEVERE, "Player hook " + currentHook.toString() + " failed.", e);
// Clean up as much as possible
try {
if (injector != null)
injector.cleanupAll();
} catch (Exception e2) {
logger.log(Level.WARNING, "Cleaing up after player hook failed.", e);
}
if (currentHook.ordinal() > 0) {
// Choose the previous player hook type
currentHook = PlayerInjectHooks.values()[currentHook.ordinal() - 1];
logger.log(Level.INFO, "Switching to " + currentHook.toString() + " instead.");
} else {
// UTTER FAILURE
playerInjection.put(player, null);
return;
}
}
}
// Update values
if (injector != null)
lastSuccessfulHook = injector;
if (currentHook != playerHook || firstPlayer)
setPlayerHook(currentHook);
}
}
/**
* Unregisters the given player.
* @param player - player to unregister.
*/
public void uninjectPlayer(Player player) {
if (!hasClosed && player != null) {
PlayerInjector injector = playerInjection.get(player);
if (injector != null) {
DataInputStream input = injector.getInputStream(true);
injector.cleanupAll();
playerInjection.remove(player);
connectionLookup.remove(input);
}
}
}
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
}
private PlayerInjector getInjector(Player player) {
if (!playerInjection.containsKey(player)) {
// What? Try to inject again.
injectPlayer(player);
}
PlayerInjector injector = playerInjection.get(player);
// Check that the injector was sucessfully added
if (injector != null)
return injector;
else
throw new IllegalArgumentException("Player has no injected handler.");
}
/**
* Determine if the given listeners are valid.
* @param listeners - listeners to check.
*/
public void checkListener(Set<PacketListener> listeners) {
// Make sure the current listeners are compatible
if (lastSuccessfulHook != null) {
for (PacketListener listener : listeners) {
try {
checkListener(listener);
} catch (IllegalStateException e) {
logger.log(Level.WARNING, "Unsupported listener.", e);
}
}
}
}
/**
* Determine if a listener is valid or not.
* @param listener - listener to check.
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
*/
public void checkListener(PacketListener listener) {
try {
if (lastSuccessfulHook != null)
lastSuccessfulHook.checkListener(listener);
} catch (Exception e) {
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
}
}
/**
* Process a packet as if it were sent by the given player.
* @param player - the sender.
* @param mcPacket - the packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException {
PlayerInjector injector = getInjector(player);
injector.processPacket(mcPacket);
}
/**
* Retrieve the current list of registered sending listeners.
* @return List of the sending listeners's packet IDs.
*/
public Set<Integer> getSendingFilters() {
return ImmutableSet.copyOf(sendingFilters);
}
/**
* Retrieve the current logger.
* @return Error logger.
*/
public Logger getLogger() {
return logger;
}
public void close() {
// Guard
if (hasClosed || playerInjection == null)
return;
// Remove everything
for (PlayerInjector injection : playerInjection.values()) {
if (injection != null) {
injection.cleanupAll();
}
}
// Remove server handler
serverInjection.cleanupAll();
hasClosed = true;
playerInjection.clear();
connectionLookup.clear();
invoker = null;
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.io.DataInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.Packet;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
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 {
/**
* The current player phase.
* @author Kristian
*/
public enum GamePhase {
LOGIN,
PLAYING,
CLOSING,
}
// Server connection injection
private InjectedServerConnection serverInjection;
// The last successful player hook
private PlayerInjector lastSuccessfulHook;
// Player injection
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
// Player injection type
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
// Error logger
private Logger logger;
// Whether or not we're closing
private boolean hasClosed;
// Used to invoke events
private ListenerInvoker invoker;
// Enabled packet filters
private Set<Integer> sendingFilters = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
// The class loader we're using
private ClassLoader classLoader;
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
this.classLoader = classLoader;
this.logger = logger;
this.invoker = invoker;
this.serverInjection = new InjectedServerConnection(logger, server);
}
/**
* Retrieves how the server packets are read.
* @return Injection method for reading server packets.
*/
public PlayerInjectHooks getPlayerHook() {
return playerHook;
}
/**
* Add an underlying packet handler of the given ID.
* @param packetID - packet ID to register.
*/
public void addPacketHandler(int packetID) {
sendingFilters.add(packetID);
}
/**
* Remove an underlying packet handler of ths ID.
* @param packetID - packet ID to unregister.
*/
public void removePacketHandler(int packetID) {
sendingFilters.remove(packetID);
}
/**
* Sets how the server packets are read.
* @param playerHook - the new injection method for reading server packets.
*/
public void setPlayerHook(PlayerInjectHooks playerHook) {
this.playerHook = playerHook;
}
/**
* Used to construct a player hook.
* @param player - the player to hook.
* @param hook - the hook type.
* @return A new player hoook
* @throws IllegalAccessException Unable to do our reflection magic.
*/
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
// Construct the correct player hook
switch (hook) {
case NETWORK_HANDLER_FIELDS:
return new NetworkFieldInjector(classLoader, logger, player, invoker, sendingFilters);
case NETWORK_MANAGER_OBJECT:
return new NetworkObjectInjector(logger, player, invoker, sendingFilters);
case NETWORK_SERVER_OBJECT:
return new NetworkServerInjector(classLoader, logger, player, invoker, sendingFilters, serverInjection);
default:
throw new IllegalArgumentException("Cannot construct a player injector.");
}
}
public Player getPlayerByConnection(DataInputStream inputStream) {
return connectionLookup.get(inputStream);
}
private PlayerInjectHooks getInjectorType(PlayerInjector injector) {
return injector != null ? injector.getHookType() : PlayerInjectHooks.NONE;
}
/**
* Initialize a player hook, allowing us to read server packets.
* @param player - player to hook.
*/
public void injectPlayer(Player player, GamePhase phase) {
PlayerInjector injector = playerInjection.get(player);
PlayerInjectHooks tempHook = playerHook;
PlayerInjectHooks permanentHook = tempHook;
// See if we need to inject something else
boolean invalidInjector = injector != null ? !injector.canInject(phase) : false;
// Don't inject if the class has closed
if (!hasClosed && player != null && (tempHook != getInjectorType(injector) || invalidInjector)) {
while (tempHook != PlayerInjectHooks.NONE) {
// Whether or not the current hook method failed completely
boolean hookFailed = false;
// Remove the previous hook, if any
cleanupHook(injector);
try {
injector = getHookInstance(player, tempHook);
// Make sure this injection method supports the current game phase
if (injector.canInject(phase)) {
injector.initialize();
injector.injectManager();
DataInputStream inputStream = injector.getInputStream(false);
if (!player.isOnline() || inputStream == null) {
throw new PlayerLoggedOutException();
}
connectionLookup.put(inputStream, player);
break;
}
} catch (PlayerLoggedOutException e) {
throw e;
} catch (Exception e) {
// Mark this injection attempt as a failure
logger.log(Level.SEVERE, "Player hook " + tempHook.toString() + " failed.", e);
hookFailed = true;
}
// Choose the previous player hook type
tempHook = PlayerInjectHooks.values()[tempHook.ordinal() - 1];
logger.log(Level.INFO, "Switching to " + tempHook.toString() + " instead.");
// Check for UTTER FAILURE
if (tempHook == PlayerInjectHooks.NONE) {
cleanupHook(injector);
injector = null;
hookFailed = true;
}
// Should we set the default hook method too?
if (hookFailed) {
permanentHook = tempHook;
}
}
// Update values
if (injector != null)
lastSuccessfulHook = injector;
if (permanentHook != playerHook)
setPlayerHook(tempHook);
// Save last injector
playerInjection.put(player, injector);
}
}
private void cleanupHook(PlayerInjector injector) {
// Clean up as much as possible
try {
if (injector != null)
injector.cleanupAll();
} catch (Exception e2) {
logger.log(Level.WARNING, "Cleaing up after player hook failed.", e2);
}
}
/**
* Unregisters the given player.
* @param player - player to unregister.
*/
public void uninjectPlayer(Player player) {
if (!hasClosed && player != null) {
PlayerInjector injector = playerInjection.get(player);
if (injector != null) {
DataInputStream input = injector.getInputStream(true);
injector.cleanupAll();
playerInjection.remove(player);
connectionLookup.remove(input);
}
}
}
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
}
private PlayerInjector getInjector(Player player) {
if (!playerInjection.containsKey(player)) {
// What? Try to inject again.
injectPlayer(player);
}
PlayerInjector injector = playerInjection.get(player);
// Check that the injector was sucessfully added
if (injector != null)
return injector;
else
throw new IllegalArgumentException("Player has no injected handler.");
}
/**
* Determine if the given listeners are valid.
* @param listeners - listeners to check.
*/
public void checkListener(Set<PacketListener> listeners) {
// Make sure the current listeners are compatible
if (lastSuccessfulHook != null) {
for (PacketListener listener : listeners) {
try {
checkListener(listener);
} catch (IllegalStateException e) {
logger.log(Level.WARNING, "Unsupported listener.", e);
}
}
}
}
/**
* Determine if a listener is valid or not.
* @param listener - listener to check.
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
*/
public void checkListener(PacketListener listener) {
try {
if (lastSuccessfulHook != null)
lastSuccessfulHook.checkListener(listener);
} catch (Exception e) {
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
}
}
/**
* Process a packet as if it were sent by the given player.
* @param player - the sender.
* @param mcPacket - the packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException {
PlayerInjector injector = getInjector(player);
injector.processPacket(mcPacket);
}
/**
* Retrieve the current list of registered sending listeners.
* @return List of the sending listeners's packet IDs.
*/
public Set<Integer> getSendingFilters() {
return ImmutableSet.copyOf(sendingFilters);
}
/**
* Retrieve the current logger.
* @return Error logger.
*/
public Logger getLogger() {
return logger;
}
public void close() {
// Guard
if (hasClosed || playerInjection == null)
return;
// Remove everything
for (PlayerInjector injection : playerInjection.values()) {
if (injection != null) {
injection.cleanupAll();
}
}
// Remove server handler
serverInjection.cleanupAll();
hasClosed = true;
playerInjection.clear();
connectionLookup.clear();
invoker = null;
}
}

Datei anzeigen

@ -1,340 +1,348 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.io.DataInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Factory;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
abstract class PlayerInjector {
// Cache previously retrieved fields
protected static Field serverHandlerField;
protected static Field proxyServerField;
protected static Field networkManagerField;
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<Object> networkModifier;
// And methods
protected static Method queueMethod;
protected static Method processMethod;
protected Player player;
protected boolean hasInitialized;
// 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
protected ListenerInvoker invoker;
// Previous data input
protected DataInputStream cachedInput;
// Handle errors
protected Logger logger;
public PlayerInjector(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException {
this.logger = logger;
this.player = player;
this.invoker = invoker;
}
/**
* Retrieve the notch (NMS) entity player object.
* @return Notch player object.
*/
protected EntityPlayer getEntityPlayer() {
CraftPlayer craft = (CraftPlayer) player;
return craft.getHandle();
}
/**
* Initialize all fields for this player injector, if it hasn't already.
* @throws IllegalAccessException An error has occured.
*/
public void initialize() throws IllegalAccessException {
EntityPlayer notchEntity = getEntityPlayer();
if (!hasInitialized) {
// Do this first, in case we encounter an exception
hasInitialized = true;
// Retrieve the server handler
if (serverHandlerField == null) {
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler");
proxyServerField = getProxyField(notchEntity, serverHandlerField);
}
// Yo dawg
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
serverHandler = serverHandlerRef.getValue();
// Next, get the network manager
if (networkManagerField == null)
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
networkManagerRef = new VolatileField(networkManagerField, serverHandler);
networkManager = networkManagerRef.getValue();
// Create the network manager modifier from the actual object type
if (networkManager != null && networkModifier == null)
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
// And the queue method
if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(networkManagerField.getType()).
getMethodByParameters("queue", Packet.class );
// And the data input stream that we'll use to identify a player
if (inputField == null)
inputField = FuzzyReflection.fromObject(networkManager, true).
getFieldByType("java\\.io\\.DataInputStream");
}
}
/**
* 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 {
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
// Is this a Minecraft hook?
if (handler != null && !handler.getClass().getName().startsWith("net.minecraft.server")) {
// This is our proxy object
if (handler instanceof Factory)
return null;
hasProxyType = true;
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
// No? Is it a Proxy type?
try {
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
// It might be
return reflection.getFieldByType(".*NetServerHandler");
} catch (RuntimeException e) {
// Damn
}
}
} catch (IllegalAccessException e) {
logger.warning("Unable to load server handler from proxy type.");
}
// Nope, just go with it
return null;
}
/**
* Retrieves the current net handler for this player.
* @return Current net handler.
* @throws IllegalAccessException Unable to find or retrieve net handler.
*/
protected Object getNetHandler() throws IllegalAccessException {
// What a mess
try {
if (netHandlerField == null)
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType("net\\.minecraft\\.NetHandler");
} catch (RuntimeException e1) {
// Swallow it
}
// Second attempt
if (netHandlerField == null) {
try {
// Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
} catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
}
}
// Get the handler
if (netHandler == null)
netHandler = FieldUtils.readField(netHandlerField, networkManager, true);
return netHandler;
}
/**
* Processes the given packet as if it was transmitted by the current player.
* @param packet - packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException {
Object netHandler = getNetHandler();
// Get the process method
if (processMethod == null) {
try {
processMethod = FuzzyReflection.fromClass(Packet.class).
getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
}
}
// We're ready
try {
processMethod.invoke(packet, netHandler);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Method " + processMethod.getName() + " is not compatible.");
} catch (InvocationTargetException e) {
throw e;
}
}
/**
* Send a packet to the client.
* @param packet - server packet to send.
* @param filtered - whether or not the packet will be filtered by our listeners.
* @param InvocationTargetException If an error occured when sending the packet.
*/
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException;
/**
* Inject a hook to catch packets sent to the current player.
*/
public abstract void injectManager();
/**
* Remove all hooks and modifications.
*/
public abstract void cleanupAll();
/**
* Determine if this inject method can even be attempted.
* @return TRUE if can be attempted, though possibly with failure, FALSE otherwise.
*/
public abstract boolean canInject();
/**
* Invoked before a new listener is registered.
* <p>
* The player injector should throw an exception if this listener cannot be properly supplied with packet events.
* @param listener - the listener that is about to be registered.
*/
public abstract void checkListener(PacketListener listener);
/**
* Allows a packet to be recieved by the listeners.
* @param packet - packet to recieve.
* @return The given packet, or the packet replaced by the listeners.
*/
public Packet handlePacketRecieved(Packet packet) {
// Get the packet ID too
Integer id = invoker.getPacketID(packet);
// Make sure we're listening
if (id != null && hasListener(id)) {
// A packet has been sent guys!
PacketContainer container = new PacketContainer(id, packet);
PacketEvent event = PacketEvent.fromServer(invoker, container, player);
invoker.invokePacketSending(event);
// Cancelling is pretty simple. Just ignore the packet.
if (event.isCancelled())
return null;
// Right, remember to replace the packet again
return event.getPacket().getHandle();
}
return packet;
}
/**
* Determine if the given injector is listening for this packet ID.
* @param packetID - packet ID to check.
* @return TRUE if it is, FALSE oterhwise.
*/
protected abstract boolean hasListener(int packetID);
/**
* 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) {
if (inputField == null)
throw new IllegalStateException("Input field is NULL.");
if (networkManager == null)
throw new IllegalStateException("Network manager is NULL.");
// Get the associated input stream
try {
if (cache && cachedInput != null)
return cachedInput;
// Save to cache
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager, true);
return cachedInput;
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to read input stream.", e);
}
}
}
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.player;
import java.io.DataInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.Packet;
import net.sf.cglib.proxy.Factory;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.VolatileField;
abstract class PlayerInjector {
// Cache previously retrieved fields
protected static Field serverHandlerField;
protected static Field proxyServerField;
protected static Field networkManagerField;
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<Object> networkModifier;
// And methods
protected static Method queueMethod;
protected static Method processMethod;
protected Player player;
protected boolean hasInitialized;
// 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
protected ListenerInvoker invoker;
// Previous data input
protected DataInputStream cachedInput;
// Handle errors
protected Logger logger;
public PlayerInjector(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException {
this.logger = logger;
this.player = player;
this.invoker = invoker;
}
/**
* Retrieve the notch (NMS) entity player object.
* @return Notch player object.
*/
protected EntityPlayer getEntityPlayer() {
CraftPlayer craft = (CraftPlayer) player;
return craft.getHandle();
}
/**
* Initialize all fields for this player injector, if it hasn't already.
* @throws IllegalAccessException An error has occured.
*/
public void initialize() throws IllegalAccessException {
EntityPlayer notchEntity = getEntityPlayer();
if (!hasInitialized) {
// Do this first, in case we encounter an exception
hasInitialized = true;
// Retrieve the server handler
if (serverHandlerField == null) {
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler");
proxyServerField = getProxyField(notchEntity, serverHandlerField);
}
// Yo dawg
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
serverHandler = serverHandlerRef.getValue();
// Next, get the network manager
if (networkManagerField == null)
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
networkManagerRef = new VolatileField(networkManagerField, serverHandler);
networkManager = networkManagerRef.getValue();
// Create the network manager modifier from the actual object type
if (networkManager != null && networkModifier == null)
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
// And the queue method
if (queueMethod == null)
queueMethod = FuzzyReflection.fromClass(networkManagerField.getType()).
getMethodByParameters("queue", Packet.class );
// And the data input stream that we'll use to identify a player
if (inputField == null)
inputField = FuzzyReflection.fromObject(networkManager, true).
getFieldByType("java\\.io\\.DataInputStream");
}
}
/**
* 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 {
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
// Is this a Minecraft hook?
if (handler != null && !handler.getClass().getName().startsWith("net.minecraft.server")) {
// This is our proxy object
if (handler instanceof Factory)
return null;
hasProxyType = true;
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
// No? Is it a Proxy type?
try {
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
// It might be
return reflection.getFieldByType(".*NetServerHandler");
} catch (RuntimeException e) {
// Damn
}
}
} catch (IllegalAccessException e) {
logger.warning("Unable to load server handler from proxy type.");
}
// Nope, just go with it
return null;
}
/**
* Retrieves the current net handler for this player.
* @return Current net handler.
* @throws IllegalAccessException Unable to find or retrieve net handler.
*/
protected Object getNetHandler() throws IllegalAccessException {
// What a mess
try {
if (netHandlerField == null)
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType("net\\.minecraft\\.NetHandler");
} catch (RuntimeException e1) {
// Swallow it
}
// Second attempt
if (netHandlerField == null) {
try {
// Well, that sucks. Try just Minecraft objects then.
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
} catch (RuntimeException e2) {
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
}
}
// Get the handler
if (netHandler == null)
netHandler = FieldUtils.readField(netHandlerField, networkManager, true);
return netHandler;
}
/**
* Processes the given packet as if it was transmitted by the current player.
* @param packet - packet to process.
* @throws IllegalAccessException If the reflection machinery failed.
* @throws InvocationTargetException If the underlying method caused an error.
*/
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException {
Object netHandler = getNetHandler();
// Get the process method
if (processMethod == null) {
try {
processMethod = FuzzyReflection.fromClass(Packet.class).
getMethodByParameters("processPacket", netHandlerField.getType());
} catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
}
}
// We're ready
try {
processMethod.invoke(packet, netHandler);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Method " + processMethod.getName() + " is not compatible.");
} catch (InvocationTargetException e) {
throw e;
}
}
/**
* Send a packet to the client.
* @param packet - server packet to send.
* @param filtered - whether or not the packet will be filtered by our listeners.
* @param InvocationTargetException If an error occured when sending the packet.
*/
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException;
/**
* Inject a hook to catch packets sent to the current player.
*/
public abstract void injectManager();
/**
* Remove all hooks and modifications.
*/
public abstract void cleanupAll();
/**
* Determine if this inject method can even be attempted.
* @return TRUE if can be attempted, though possibly with failure, FALSE otherwise.
*/
public abstract boolean canInject(GamePhase state);
/**
* Retrieve the hook type this class represents.
* @return Hook type this class represents.
*/
public abstract PlayerInjectHooks getHookType();
/**
* Invoked before a new listener is registered.
* <p>
* The player injector should throw an exception if this listener cannot be properly supplied with packet events.
* @param listener - the listener that is about to be registered.
*/
public abstract void checkListener(PacketListener listener);
/**
* Allows a packet to be recieved by the listeners.
* @param packet - packet to recieve.
* @return The given packet, or the packet replaced by the listeners.
*/
public Packet handlePacketRecieved(Packet packet) {
// Get the packet ID too
Integer id = invoker.getPacketID(packet);
// Make sure we're listening
if (id != null && hasListener(id)) {
// A packet has been sent guys!
PacketContainer container = new PacketContainer(id, packet);
PacketEvent event = PacketEvent.fromServer(invoker, container, player);
invoker.invokePacketSending(event);
// Cancelling is pretty simple. Just ignore the packet.
if (event.isCancelled())
return null;
// Right, remember to replace the packet again
return event.getPacket().getHandle();
}
return packet;
}
/**
* Determine if the given injector is listening for this packet ID.
* @param packetID - packet ID to check.
* @return TRUE if it is, FALSE oterhwise.
*/
protected abstract boolean hasListener(int packetID);
/**
* 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) {
if (inputField == null)
throw new IllegalStateException("Input field is NULL.");
if (networkManager == null)
throw new IllegalStateException("Network manager is NULL.");
// Get the associated input stream
try {
if (cache && cachedInput != null)
return cachedInput;
// Save to cache
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager, true);
return cachedInput;
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to read input stream.", e);
}
}
}