First test of the pre-login injector.
Dieser Commit ist enthalten in:
Ursprung
39805c5502
Commit
9b4b161602
@ -2,7 +2,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.comphenix.protocol</groupId>
|
<groupId>com.comphenix.protocol</groupId>
|
||||||
<artifactId>ProtocolLib</artifactId>
|
<artifactId>ProtocolLib</artifactId>
|
||||||
<version>1.3.3</version>
|
<version>1.3.3-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<description>Provides read/write access to the Minecraft protocol.</description>
|
<description>Provides read/write access to the Minecraft protocol.</description>
|
||||||
|
|
||||||
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -1,196 +1,204 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* 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
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* 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;
|
* 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.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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;
|
* 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
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.VolatileField;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.google.common.collect.Sets;
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
import net.minecraft.server.Packet;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
/**
|
import net.minecraft.server.Packet;
|
||||||
* Injection hook that overrides the packet queue lists in NetworkHandler.
|
|
||||||
*
|
/**
|
||||||
* @author Kristian
|
* Injection hook that overrides the packet queue lists in NetworkHandler.
|
||||||
*/
|
*
|
||||||
class NetworkFieldInjector extends PlayerInjector {
|
* @author Kristian
|
||||||
|
*/
|
||||||
/**
|
class NetworkFieldInjector extends PlayerInjector {
|
||||||
* Marker interface that indicates a packet is fake and should not be processed.
|
|
||||||
* @author Kristian
|
/**
|
||||||
*/
|
* Marker interface that indicates a packet is fake and should not be processed.
|
||||||
public interface FakePacket {
|
* @author Kristian
|
||||||
// Nothing
|
*/
|
||||||
}
|
public interface FakePacket {
|
||||||
|
// Nothing
|
||||||
// Packets to ignore
|
}
|
||||||
private Set<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>());
|
|
||||||
|
// Packets to ignore
|
||||||
// Overridden fields
|
private Set<Packet> ignoredPackets = Sets.newSetFromMap(new ConcurrentHashMap<Packet, Boolean>());
|
||||||
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
|
|
||||||
|
// Overridden fields
|
||||||
// Sync field
|
private List<VolatileField> overridenLists = new ArrayList<VolatileField>();
|
||||||
private static Field syncField;
|
|
||||||
private Object syncObject;
|
// Sync field
|
||||||
|
private static Field syncField;
|
||||||
// Determine if we're listening
|
private Object syncObject;
|
||||||
private Set<Integer> sendingFilters;
|
|
||||||
|
// Determine if we're listening
|
||||||
// Used to construct proxy objects
|
private Set<Integer> sendingFilters;
|
||||||
private ClassLoader classLoader;
|
|
||||||
|
// Used to construct proxy objects
|
||||||
public NetworkFieldInjector(ClassLoader classLoader, Logger logger, Player player,
|
private ClassLoader classLoader;
|
||||||
ListenerInvoker manager, Set<Integer> sendingFilters) throws IllegalAccessException {
|
|
||||||
|
public NetworkFieldInjector(ClassLoader classLoader, Logger logger, Player player,
|
||||||
super(logger, player, manager);
|
ListenerInvoker manager, Set<Integer> sendingFilters) throws IllegalAccessException {
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.sendingFilters = sendingFilters;
|
super(logger, player, manager);
|
||||||
}
|
this.classLoader = classLoader;
|
||||||
|
this.sendingFilters = sendingFilters;
|
||||||
@Override
|
}
|
||||||
protected boolean hasListener(int packetID) {
|
|
||||||
return sendingFilters.contains(packetID);
|
@Override
|
||||||
}
|
protected boolean hasListener(int packetID) {
|
||||||
|
return sendingFilters.contains(packetID);
|
||||||
@Override
|
}
|
||||||
public synchronized void initialize() throws IllegalAccessException {
|
|
||||||
super.initialize();
|
@Override
|
||||||
|
public synchronized void initialize() throws IllegalAccessException {
|
||||||
// Get the sync field as well
|
super.initialize();
|
||||||
if (hasInitialized) {
|
|
||||||
if (syncField == null)
|
// Get the sync field as well
|
||||||
syncField = FuzzyReflection.fromObject(networkManager, true).getFieldByType("java\\.lang\\.Object");
|
if (hasInitialized) {
|
||||||
syncObject = FieldUtils.readField(syncField, networkManager, true);
|
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 {
|
|
||||||
|
@Override
|
||||||
if (networkManager != null) {
|
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||||
try {
|
|
||||||
if (!filtered) {
|
if (networkManager != null) {
|
||||||
ignoredPackets.add(packet);
|
try {
|
||||||
}
|
if (!filtered) {
|
||||||
|
ignoredPackets.add(packet);
|
||||||
// Note that invocation target exception is a wrapper for a checked exception
|
}
|
||||||
queueMethod.invoke(networkManager, packet);
|
|
||||||
|
// Note that invocation target exception is a wrapper for a checked exception
|
||||||
} catch (IllegalArgumentException e) {
|
queueMethod.invoke(networkManager, packet);
|
||||||
throw e;
|
|
||||||
} catch (InvocationTargetException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (InvocationTargetException e) {
|
||||||
throw new IllegalStateException("Unable to access queue method.", e);
|
throw e;
|
||||||
}
|
} catch (IllegalAccessException e) {
|
||||||
} else {
|
throw new IllegalStateException("Unable to access queue method.", e);
|
||||||
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
|
}
|
||||||
}
|
} 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
|
@Override
|
||||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
|
public void checkListener(PacketListener listener) {
|
||||||
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
|
// Unfortunately, we don't support chunk packets
|
||||||
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
|
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() {
|
|
||||||
|
@Override
|
||||||
if (networkManager != null) {
|
public void injectManager() {
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
if (networkManager != null) {
|
||||||
StructureModifier<List> list = networkModifier.withType(List.class);
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
// Subclass both send queues
|
StructureModifier<List> list = networkModifier.withType(List.class);
|
||||||
for (Field field : list.getFields()) {
|
|
||||||
VolatileField overwriter = new VolatileField(field, networkManager, true);
|
// Subclass both send queues
|
||||||
|
for (Field field : list.getFields()) {
|
||||||
@SuppressWarnings("unchecked")
|
VolatileField overwriter = new VolatileField(field, networkManager, true);
|
||||||
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
synchronized(syncObject) {
|
List<Packet> minecraftList = (List<Packet>) overwriter.getOldValue();
|
||||||
// The list we'll be inserting
|
|
||||||
List<Packet> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets);
|
synchronized(syncObject) {
|
||||||
|
// The list we'll be inserting
|
||||||
// Add every previously stored packet
|
List<Packet> hackedList = new InjectedArrayList(classLoader, this, ignoredPackets);
|
||||||
for (Packet packet : minecraftList) {
|
|
||||||
hackedList.add(packet);
|
// Add every previously stored packet
|
||||||
}
|
for (Packet packet : minecraftList) {
|
||||||
|
hackedList.add(packet);
|
||||||
// Don' keep stale packets around
|
}
|
||||||
minecraftList.clear();
|
|
||||||
overwriter.setValue(Collections.synchronizedList(hackedList));
|
// Don' keep stale packets around
|
||||||
}
|
minecraftList.clear();
|
||||||
|
overwriter.setValue(Collections.synchronizedList(hackedList));
|
||||||
overridenLists.add(overwriter);
|
}
|
||||||
}
|
|
||||||
}
|
overridenLists.add(overwriter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@SuppressWarnings("unchecked")
|
}
|
||||||
public void cleanupAll() {
|
|
||||||
// Clean up
|
@SuppressWarnings("unchecked")
|
||||||
for (VolatileField overriden : overridenLists) {
|
public void cleanupAll() {
|
||||||
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
|
// Clean up
|
||||||
List<Packet> hacketList = (List<Packet>) overriden.getValue();
|
for (VolatileField overriden : overridenLists) {
|
||||||
|
List<Packet> minecraftList = (List<Packet>) overriden.getOldValue();
|
||||||
if (minecraftList == hacketList) {
|
List<Packet> hacketList = (List<Packet>) overriden.getValue();
|
||||||
return;
|
|
||||||
}
|
if (minecraftList == hacketList) {
|
||||||
|
return;
|
||||||
// Get a lock before we modify the list
|
}
|
||||||
synchronized(syncObject) {
|
|
||||||
try {
|
// Get a lock before we modify the list
|
||||||
// Copy over current packets
|
synchronized(syncObject) {
|
||||||
for (Packet packet : (List<Packet>) overriden.getValue()) {
|
try {
|
||||||
minecraftList.add(packet);
|
// Copy over current packets
|
||||||
}
|
for (Packet packet : (List<Packet>) overriden.getValue()) {
|
||||||
} finally {
|
minecraftList.add(packet);
|
||||||
overriden.revertValue();
|
}
|
||||||
}
|
} finally {
|
||||||
}
|
overriden.revertValue();
|
||||||
}
|
}
|
||||||
overridenLists.clear();
|
}
|
||||||
}
|
}
|
||||||
|
overridenLists.clear();
|
||||||
@Override
|
}
|
||||||
public boolean canInject() {
|
|
||||||
return true;
|
@Override
|
||||||
}
|
public boolean canInject(GamePhase phase) {
|
||||||
}
|
// All phases should work
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getHookType() {
|
||||||
|
return PlayerInjectHooks.NETWORK_HANDLER_FIELDS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,143 +1,151 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* 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
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* 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;
|
* 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.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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;
|
* 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
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
import net.minecraft.server.Packet;
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.Packets;
|
import com.comphenix.protocol.Packets;
|
||||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
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
|
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
|
||||||
*/
|
*
|
||||||
class NetworkObjectInjector extends PlayerInjector {
|
* @author Kristian
|
||||||
// Determine if we're listening
|
*/
|
||||||
private Set<Integer> sendingFilters;
|
class NetworkObjectInjector extends PlayerInjector {
|
||||||
|
// Determine if we're listening
|
||||||
public NetworkObjectInjector(Logger logger, Player player, ListenerInvoker invoker, Set<Integer> sendingFilters) throws IllegalAccessException {
|
private Set<Integer> sendingFilters;
|
||||||
super(logger, player, invoker);
|
|
||||||
this.sendingFilters = 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
|
||||||
}
|
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();
|
@Override
|
||||||
|
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||||
if (networkDelegate != null) {
|
Object networkDelegate = filtered ? networkManagerRef.getValue() : networkManagerRef.getOldValue();
|
||||||
try {
|
|
||||||
// Note that invocation target exception is a wrapper for a checked exception
|
if (networkDelegate != null) {
|
||||||
queueMethod.invoke(networkDelegate, packet);
|
try {
|
||||||
|
// Note that invocation target exception is a wrapper for a checked exception
|
||||||
} catch (IllegalArgumentException e) {
|
queueMethod.invoke(networkDelegate, packet);
|
||||||
throw e;
|
|
||||||
} catch (InvocationTargetException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (InvocationTargetException e) {
|
||||||
throw new IllegalStateException("Unable to access queue method.", e);
|
throw e;
|
||||||
}
|
} catch (IllegalAccessException e) {
|
||||||
} else {
|
throw new IllegalStateException("Unable to access queue method.", e);
|
||||||
throw new IllegalStateException("Unable to load network mananager. Cannot send packet.");
|
}
|
||||||
}
|
} 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
|
@Override
|
||||||
if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
|
public void checkListener(PacketListener listener) {
|
||||||
Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
|
// Unfortunately, we don't support chunk packets
|
||||||
throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
|
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() {
|
|
||||||
|
@Override
|
||||||
if (networkManager != null) {
|
public void injectManager() {
|
||||||
final Class<?> networkInterface = networkManagerField.getType();
|
|
||||||
final Object networkDelegate = networkManagerRef.getOldValue();
|
if (networkManager != null) {
|
||||||
|
final Class<?> networkInterface = networkManagerField.getType();
|
||||||
if (!networkInterface.isInterface()) {
|
final Object networkDelegate = networkManagerRef.getOldValue();
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"Must use CraftBukkit 1.3.0 or later to inject into into NetworkMananger.");
|
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() {
|
// Create our proxy object
|
||||||
|
Object networkProxy = Proxy.newProxyInstance(networkInterface.getClassLoader(),
|
||||||
@Override
|
new Class<?>[] { networkInterface }, new InvocationHandler() {
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
// OH OH! The queue method!
|
@Override
|
||||||
if (method.equals(queueMethod)) {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
Packet packet = (Packet) args[0];
|
// OH OH! The queue method!
|
||||||
|
if (method.equals(queueMethod)) {
|
||||||
if (packet != null) {
|
Packet packet = (Packet) args[0];
|
||||||
packet = handlePacketRecieved(packet);
|
|
||||||
|
if (packet != null) {
|
||||||
// A NULL packet indicate cancelling
|
packet = handlePacketRecieved(packet);
|
||||||
if (packet != null)
|
|
||||||
args[0] = packet;
|
// A NULL packet indicate cancelling
|
||||||
else
|
if (packet != null)
|
||||||
return null;
|
args[0] = packet;
|
||||||
}
|
else
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
// Delegate to our underlying class
|
}
|
||||||
try {
|
|
||||||
return method.invoke(networkDelegate, args);
|
// Delegate to our underlying class
|
||||||
} catch (InvocationTargetException e) {
|
try {
|
||||||
throw e.getCause();
|
return method.invoke(networkDelegate, args);
|
||||||
}
|
} catch (InvocationTargetException e) {
|
||||||
}
|
throw e.getCause();
|
||||||
});
|
}
|
||||||
|
}
|
||||||
// Inject it, if we can.
|
});
|
||||||
networkManagerRef.setValue(networkProxy);
|
|
||||||
}
|
// Inject it, if we can.
|
||||||
}
|
networkManagerRef.setValue(networkProxy);
|
||||||
|
}
|
||||||
@Override
|
}
|
||||||
public void cleanupAll() {
|
|
||||||
// Clean up
|
@Override
|
||||||
networkManagerRef.revertValue();
|
public void cleanupAll() {
|
||||||
}
|
// Clean up
|
||||||
|
networkManagerRef.revertValue();
|
||||||
@Override
|
}
|
||||||
public boolean canInject() {
|
|
||||||
return true;
|
@Override
|
||||||
}
|
public boolean canInject(GamePhase phase) {
|
||||||
}
|
// Works for all phases
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlayerInjectHooks getHookType() {
|
||||||
|
return PlayerInjectHooks.NETWORK_MANAGER_OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,266 +1,274 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* 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
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* 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;
|
* 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.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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;
|
* 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
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import net.minecraft.server.Packet;
|
import net.minecraft.server.Packet;
|
||||||
import net.sf.cglib.proxy.Callback;
|
import net.sf.cglib.proxy.Callback;
|
||||||
import net.sf.cglib.proxy.CallbackFilter;
|
import net.sf.cglib.proxy.CallbackFilter;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
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 net.sf.cglib.proxy.NoOp;
|
import net.sf.cglib.proxy.NoOp;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||||
import com.comphenix.protocol.reflect.ObjectCloner;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.VolatileField;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
import com.comphenix.protocol.reflect.ObjectCloner;
|
||||||
import com.comphenix.protocol.reflect.instances.ExistingGenerator;
|
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
|
* Represents a player hook into the NetServerHandler class.
|
||||||
*/
|
*
|
||||||
public class NetworkServerInjector extends PlayerInjector {
|
* @author Kristian
|
||||||
|
*/
|
||||||
private static Method sendPacketMethod;
|
public class NetworkServerInjector extends PlayerInjector {
|
||||||
private InjectedServerConnection serverInjection;
|
|
||||||
|
private static Method sendPacketMethod;
|
||||||
// Determine if we're listening
|
private InjectedServerConnection serverInjection;
|
||||||
private Set<Integer> sendingFilters;
|
|
||||||
|
// Determine if we're listening
|
||||||
// Used to create proxy objects
|
private Set<Integer> sendingFilters;
|
||||||
private ClassLoader classLoader;
|
|
||||||
|
// Used to create proxy objects
|
||||||
public NetworkServerInjector(
|
private ClassLoader classLoader;
|
||||||
ClassLoader classLoader, Logger logger, Player player,
|
|
||||||
ListenerInvoker invoker, Set<Integer> sendingFilters,
|
public NetworkServerInjector(
|
||||||
InjectedServerConnection serverInjection) throws IllegalAccessException {
|
ClassLoader classLoader, Logger logger, Player player,
|
||||||
|
ListenerInvoker invoker, Set<Integer> sendingFilters,
|
||||||
super(logger, player, invoker);
|
InjectedServerConnection serverInjection) throws IllegalAccessException {
|
||||||
this.classLoader = classLoader;
|
|
||||||
this.sendingFilters = sendingFilters;
|
super(logger, player, invoker);
|
||||||
this.serverInjection = serverInjection;
|
this.classLoader = classLoader;
|
||||||
}
|
this.sendingFilters = sendingFilters;
|
||||||
|
this.serverInjection = serverInjection;
|
||||||
@Override
|
}
|
||||||
protected boolean hasListener(int packetID) {
|
|
||||||
return sendingFilters.contains(packetID);
|
@Override
|
||||||
}
|
protected boolean hasListener(int packetID) {
|
||||||
|
return sendingFilters.contains(packetID);
|
||||||
@Override
|
}
|
||||||
public void initialize() throws IllegalAccessException {
|
|
||||||
super.initialize();
|
@Override
|
||||||
|
public void initialize() throws IllegalAccessException {
|
||||||
// Get the send packet method!
|
super.initialize();
|
||||||
if (hasInitialized) {
|
|
||||||
if (sendPacketMethod == null)
|
// Get the send packet method!
|
||||||
sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*");
|
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();
|
@Override
|
||||||
|
public void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException {
|
||||||
if (serverDeleage != null) {
|
Object serverDeleage = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
|
||||||
try {
|
|
||||||
// Note that invocation target exception is a wrapper for a checked exception
|
if (serverDeleage != null) {
|
||||||
sendPacketMethod.invoke(serverDeleage, packet);
|
try {
|
||||||
|
// Note that invocation target exception is a wrapper for a checked exception
|
||||||
} catch (IllegalArgumentException e) {
|
sendPacketMethod.invoke(serverDeleage, packet);
|
||||||
throw e;
|
|
||||||
} catch (InvocationTargetException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (IllegalAccessException e) {
|
} catch (InvocationTargetException e) {
|
||||||
throw new IllegalStateException("Unable to access send packet method.", e);
|
throw e;
|
||||||
}
|
} catch (IllegalAccessException e) {
|
||||||
} else {
|
throw new IllegalStateException("Unable to access send packet method.", e);
|
||||||
throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
|
}
|
||||||
}
|
} else {
|
||||||
}
|
throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
|
||||||
|
}
|
||||||
@Override
|
}
|
||||||
public void injectManager() {
|
|
||||||
|
@Override
|
||||||
if (serverHandlerRef == null)
|
public void injectManager() {
|
||||||
throw new IllegalStateException("Cannot find server handler.");
|
|
||||||
// Don't inject twice
|
if (serverHandlerRef == null)
|
||||||
if (serverHandlerRef.getValue() instanceof Factory)
|
throw new IllegalStateException("Cannot find server handler.");
|
||||||
return;
|
// Don't inject twice
|
||||||
|
if (serverHandlerRef.getValue() instanceof Factory)
|
||||||
if (!tryInjectManager()) {
|
return;
|
||||||
|
|
||||||
// Try to override the proxied object
|
if (!tryInjectManager()) {
|
||||||
if (proxyServerField != null) {
|
|
||||||
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
|
// Try to override the proxied object
|
||||||
serverHandler = serverHandlerRef.getValue();
|
if (proxyServerField != null) {
|
||||||
|
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
|
||||||
if (serverHandler == null)
|
serverHandler = serverHandlerRef.getValue();
|
||||||
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
|
|
||||||
|
if (serverHandler == null)
|
||||||
// Try again
|
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
|
||||||
if (tryInjectManager()) {
|
|
||||||
// It worked - probably
|
// Try again
|
||||||
return;
|
if (tryInjectManager()) {
|
||||||
}
|
// It worked - probably
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
throw new RuntimeException(
|
}
|
||||||
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
|
|
||||||
}
|
throw new RuntimeException(
|
||||||
}
|
"Cannot hook player: Unable to find a valid constructor for the NetServerHandler object.");
|
||||||
|
}
|
||||||
private boolean tryInjectManager() {
|
}
|
||||||
Class<?> serverClass = serverHandler.getClass();
|
|
||||||
|
private boolean tryInjectManager() {
|
||||||
Enhancer ex = new Enhancer();
|
Class<?> serverClass = serverHandler.getClass();
|
||||||
Callback sendPacketCallback = new MethodInterceptor() {
|
|
||||||
@Override
|
Enhancer ex = new Enhancer();
|
||||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
Callback sendPacketCallback = new MethodInterceptor() {
|
||||||
|
@Override
|
||||||
Packet packet = (Packet) args[0];
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
|
||||||
if (packet != null) {
|
Packet packet = (Packet) args[0];
|
||||||
packet = handlePacketRecieved(packet);
|
|
||||||
|
if (packet != null) {
|
||||||
// A NULL packet indicate cancelling
|
packet = handlePacketRecieved(packet);
|
||||||
if (packet != null)
|
|
||||||
args[0] = packet;
|
// A NULL packet indicate cancelling
|
||||||
else
|
if (packet != null)
|
||||||
return null;
|
args[0] = packet;
|
||||||
}
|
else
|
||||||
|
return null;
|
||||||
// Call the method directly
|
}
|
||||||
return proxy.invokeSuper(obj, args);
|
|
||||||
};
|
// Call the method directly
|
||||||
};
|
return proxy.invokeSuper(obj, args);
|
||||||
Callback noOpCallback = NoOp.INSTANCE;
|
};
|
||||||
|
};
|
||||||
ex.setClassLoader(classLoader);
|
Callback noOpCallback = NoOp.INSTANCE;
|
||||||
ex.setSuperclass(serverClass);
|
|
||||||
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
ex.setClassLoader(classLoader);
|
||||||
ex.setCallbackFilter(new CallbackFilter() {
|
ex.setSuperclass(serverClass);
|
||||||
@Override
|
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
|
||||||
public int accept(Method method) {
|
ex.setCallbackFilter(new CallbackFilter() {
|
||||||
if (method.equals(sendPacketMethod))
|
@Override
|
||||||
return 0;
|
public int accept(Method method) {
|
||||||
else
|
if (method.equals(sendPacketMethod))
|
||||||
return 1;
|
return 0;
|
||||||
}
|
else
|
||||||
});
|
return 1;
|
||||||
|
}
|
||||||
// Find the Minecraft NetServerHandler superclass
|
});
|
||||||
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
|
||||||
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
|
// Find the Minecraft NetServerHandler superclass
|
||||||
DefaultInstances serverInstances = null;
|
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
|
||||||
|
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
|
||||||
// Maybe the proxy instance can help?
|
DefaultInstances serverInstances = null;
|
||||||
Object proxyInstance = getProxyServerHandler();
|
|
||||||
|
// Maybe the proxy instance can help?
|
||||||
// Use the existing server proxy when we create one
|
Object proxyInstance = getProxyServerHandler();
|
||||||
if (proxyInstance != null && proxyInstance != serverHandler) {
|
|
||||||
serverInstances = DefaultInstances.fromArray(generator,
|
// Use the existing server proxy when we create one
|
||||||
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
|
if (proxyInstance != null && proxyInstance != serverHandler) {
|
||||||
} else {
|
serverInstances = DefaultInstances.fromArray(generator,
|
||||||
serverInstances = DefaultInstances.fromArray(generator);
|
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
|
||||||
}
|
} else {
|
||||||
|
serverInstances = DefaultInstances.fromArray(generator);
|
||||||
serverInstances.setNonNull(true);
|
}
|
||||||
serverInstances.setMaximumRecursion(1);
|
|
||||||
|
serverInstances.setNonNull(true);
|
||||||
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
|
serverInstances.setMaximumRecursion(1);
|
||||||
|
|
||||||
// Inject it now
|
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
|
||||||
if (proxyObject != null) {
|
|
||||||
// This will be done by InjectedServerConnection instead
|
// Inject it now
|
||||||
//copyTo(serverHandler, proxyObject);
|
if (proxyObject != null) {
|
||||||
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
// This will be done by InjectedServerConnection instead
|
||||||
serverHandlerRef.setValue(proxyObject);
|
//copyTo(serverHandler, proxyObject);
|
||||||
return true;
|
serverInjection.replaceServerHandler(serverHandler, proxyObject);
|
||||||
} else {
|
serverHandlerRef.setValue(proxyObject);
|
||||||
return false;
|
return true;
|
||||||
}
|
} else {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
private Object getProxyServerHandler() {
|
}
|
||||||
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
|
|
||||||
try {
|
private Object getProxyServerHandler() {
|
||||||
return FieldUtils.readField(proxyServerField, serverHandler, true);
|
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
|
||||||
} catch (Throwable e) {
|
try {
|
||||||
// Oh well
|
return FieldUtils.readField(proxyServerField, serverHandler, true);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
}
|
// Oh well
|
||||||
|
}
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
return null;
|
||||||
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
|
}
|
||||||
if (clazz.getName().startsWith("net.minecraft.server."))
|
|
||||||
return clazz;
|
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
|
||||||
else if (clazz.equals(Object.class))
|
if (clazz.getName().startsWith("net.minecraft.server."))
|
||||||
return clazz;
|
return clazz;
|
||||||
else
|
else if (clazz.equals(Object.class))
|
||||||
return getFirstMinecraftSuperClass(clazz.getSuperclass());
|
return clazz;
|
||||||
}
|
else
|
||||||
|
return getFirstMinecraftSuperClass(clazz.getSuperclass());
|
||||||
@Override
|
}
|
||||||
public void cleanupAll() {
|
|
||||||
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
|
@Override
|
||||||
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
|
public void cleanupAll() {
|
||||||
serverHandlerRef.revertValue();
|
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
|
||||||
|
ObjectCloner.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
|
||||||
try {
|
serverHandlerRef.revertValue();
|
||||||
if (getNetHandler() != null) {
|
|
||||||
// Restore packet listener
|
try {
|
||||||
try {
|
if (getNetHandler() != null) {
|
||||||
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
|
// Restore packet listener
|
||||||
} catch (IllegalAccessException e) {
|
try {
|
||||||
// Oh well
|
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
|
||||||
e.printStackTrace();
|
} catch (IllegalAccessException e) {
|
||||||
}
|
// Oh well
|
||||||
}
|
e.printStackTrace();
|
||||||
} catch (IllegalAccessException e) {
|
}
|
||||||
e.printStackTrace();
|
}
|
||||||
}
|
} catch (IllegalAccessException e) {
|
||||||
}
|
e.printStackTrace();
|
||||||
|
}
|
||||||
serverInjection.revertServerHandler(serverHandler);
|
}
|
||||||
}
|
|
||||||
|
serverInjection.revertServerHandler(serverHandler);
|
||||||
@Override
|
}
|
||||||
public void checkListener(PacketListener listener) {
|
|
||||||
// We support everything
|
@Override
|
||||||
}
|
public void checkListener(PacketListener listener) {
|
||||||
|
// We support everything
|
||||||
@Override
|
}
|
||||||
public boolean canInject() {
|
|
||||||
return true;
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,327 +1,362 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* 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
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* 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;
|
* 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.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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;
|
* 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
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import net.minecraft.server.Packet;
|
import net.minecraft.server.Packet;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketAdapter;
|
import com.comphenix.protocol.events.PacketAdapter;
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for injecting into a player's sendPacket method.
|
* Responsible for injecting into a player's sendPacket method.
|
||||||
*
|
*
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class PlayerInjectionHandler {
|
public class PlayerInjectionHandler {
|
||||||
|
|
||||||
// Server connection injection
|
/**
|
||||||
private InjectedServerConnection serverInjection;
|
* The current player phase.
|
||||||
|
* @author Kristian
|
||||||
// The last successful player hook
|
*/
|
||||||
private PlayerInjector lastSuccessfulHook;
|
public enum GamePhase {
|
||||||
|
LOGIN,
|
||||||
// Player injection
|
PLAYING,
|
||||||
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
|
CLOSING,
|
||||||
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
|
}
|
||||||
|
|
||||||
// Player injection type
|
// Server connection injection
|
||||||
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
private InjectedServerConnection serverInjection;
|
||||||
|
|
||||||
// Error logger
|
// The last successful player hook
|
||||||
private Logger logger;
|
private PlayerInjector lastSuccessfulHook;
|
||||||
|
|
||||||
// Whether or not we're closing
|
// Player injection
|
||||||
private boolean hasClosed;
|
private Map<DataInputStream, Player> connectionLookup = new ConcurrentHashMap<DataInputStream, Player>();
|
||||||
|
private Map<Player, PlayerInjector> playerInjection = new HashMap<Player, PlayerInjector>();
|
||||||
// Used to invoke events
|
|
||||||
private ListenerInvoker invoker;
|
// Player injection type
|
||||||
|
private PlayerInjectHooks playerHook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
|
||||||
// Enabled packet filters
|
|
||||||
private Set<Integer> sendingFilters = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
|
// Error logger
|
||||||
|
private Logger logger;
|
||||||
// The class loader we're using
|
|
||||||
private ClassLoader classLoader;
|
// Whether or not we're closing
|
||||||
|
private boolean hasClosed;
|
||||||
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
|
|
||||||
this.classLoader = classLoader;
|
// Used to invoke events
|
||||||
this.logger = logger;
|
private ListenerInvoker invoker;
|
||||||
this.invoker = invoker;
|
|
||||||
this.serverInjection = new InjectedServerConnection(logger, server);
|
// Enabled packet filters
|
||||||
}
|
private Set<Integer> sendingFilters = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
|
||||||
|
|
||||||
/**
|
// The class loader we're using
|
||||||
* Retrieves how the server packets are read.
|
private ClassLoader classLoader;
|
||||||
* @return Injection method for reading server packets.
|
|
||||||
*/
|
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
|
||||||
public PlayerInjectHooks getPlayerHook() {
|
this.classLoader = classLoader;
|
||||||
return playerHook;
|
this.logger = logger;
|
||||||
}
|
this.invoker = invoker;
|
||||||
|
this.serverInjection = new InjectedServerConnection(logger, server);
|
||||||
/**
|
}
|
||||||
* Add an underlying packet handler of the given ID.
|
|
||||||
* @param packetID - packet ID to register.
|
/**
|
||||||
*/
|
* Retrieves how the server packets are read.
|
||||||
public void addPacketHandler(int packetID) {
|
* @return Injection method for reading server packets.
|
||||||
sendingFilters.add(packetID);
|
*/
|
||||||
}
|
public PlayerInjectHooks getPlayerHook() {
|
||||||
|
return playerHook;
|
||||||
/**
|
}
|
||||||
* Remove an underlying packet handler of ths ID.
|
|
||||||
* @param packetID - packet ID to unregister.
|
/**
|
||||||
*/
|
* Add an underlying packet handler of the given ID.
|
||||||
public void removePacketHandler(int packetID) {
|
* @param packetID - packet ID to register.
|
||||||
sendingFilters.remove(packetID);
|
*/
|
||||||
}
|
public void addPacketHandler(int packetID) {
|
||||||
|
sendingFilters.add(packetID);
|
||||||
/**
|
}
|
||||||
* Sets how the server packets are read.
|
|
||||||
* @param playerHook - the new injection method for reading server packets.
|
/**
|
||||||
*/
|
* Remove an underlying packet handler of ths ID.
|
||||||
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
* @param packetID - packet ID to unregister.
|
||||||
this.playerHook = playerHook;
|
*/
|
||||||
}
|
public void removePacketHandler(int packetID) {
|
||||||
|
sendingFilters.remove(packetID);
|
||||||
/**
|
}
|
||||||
* Used to construct a player hook.
|
|
||||||
* @param player - the player to hook.
|
/**
|
||||||
* @param hook - the hook type.
|
* Sets how the server packets are read.
|
||||||
* @return A new player hoook
|
* @param playerHook - the new injection method for reading server packets.
|
||||||
* @throws IllegalAccessException Unable to do our reflection magic.
|
*/
|
||||||
*/
|
public void setPlayerHook(PlayerInjectHooks playerHook) {
|
||||||
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
|
this.playerHook = playerHook;
|
||||||
// Construct the correct player hook
|
}
|
||||||
switch (hook) {
|
|
||||||
case NETWORK_HANDLER_FIELDS:
|
/**
|
||||||
return new NetworkFieldInjector(classLoader, logger, player, invoker, sendingFilters);
|
* Used to construct a player hook.
|
||||||
case NETWORK_MANAGER_OBJECT:
|
* @param player - the player to hook.
|
||||||
return new NetworkObjectInjector(logger, player, invoker, sendingFilters);
|
* @param hook - the hook type.
|
||||||
case NETWORK_SERVER_OBJECT:
|
* @return A new player hoook
|
||||||
return new NetworkServerInjector(classLoader, logger, player, invoker, sendingFilters, serverInjection);
|
* @throws IllegalAccessException Unable to do our reflection magic.
|
||||||
default:
|
*/
|
||||||
throw new IllegalArgumentException("Cannot construct a player injector.");
|
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
|
||||||
}
|
// Construct the correct player hook
|
||||||
}
|
switch (hook) {
|
||||||
|
case NETWORK_HANDLER_FIELDS:
|
||||||
public Player getPlayerByConnection(DataInputStream inputStream) {
|
return new NetworkFieldInjector(classLoader, logger, player, invoker, sendingFilters);
|
||||||
return connectionLookup.get(inputStream);
|
case NETWORK_MANAGER_OBJECT:
|
||||||
}
|
return new NetworkObjectInjector(logger, player, invoker, sendingFilters);
|
||||||
|
case NETWORK_SERVER_OBJECT:
|
||||||
/**
|
return new NetworkServerInjector(classLoader, logger, player, invoker, sendingFilters, serverInjection);
|
||||||
* Initialize a player hook, allowing us to read server packets.
|
default:
|
||||||
* @param player - player to hook.
|
throw new IllegalArgumentException("Cannot construct a player injector.");
|
||||||
*/
|
}
|
||||||
public void injectPlayer(Player player) {
|
}
|
||||||
|
|
||||||
PlayerInjector injector = null;
|
public Player getPlayerByConnection(DataInputStream inputStream) {
|
||||||
PlayerInjectHooks currentHook = playerHook;
|
return connectionLookup.get(inputStream);
|
||||||
boolean firstPlayer = lastSuccessfulHook == null;
|
}
|
||||||
|
|
||||||
// Don't inject if the class has closed
|
private PlayerInjectHooks getInjectorType(PlayerInjector injector) {
|
||||||
if (!hasClosed && player != null && !playerInjection.containsKey(player)) {
|
return injector != null ? injector.getHookType() : PlayerInjectHooks.NONE;
|
||||||
while (true) {
|
}
|
||||||
try {
|
|
||||||
injector = getHookInstance(player, currentHook);
|
/**
|
||||||
injector.initialize();
|
* Initialize a player hook, allowing us to read server packets.
|
||||||
injector.injectManager();
|
* @param player - player to hook.
|
||||||
|
*/
|
||||||
DataInputStream inputStream = injector.getInputStream(false);
|
public void injectPlayer(Player player, GamePhase phase) {
|
||||||
|
|
||||||
if (!player.isOnline() || inputStream == null) {
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
throw new PlayerLoggedOutException();
|
PlayerInjectHooks tempHook = playerHook;
|
||||||
}
|
PlayerInjectHooks permanentHook = tempHook;
|
||||||
|
|
||||||
playerInjection.put(player, injector);
|
// See if we need to inject something else
|
||||||
connectionLookup.put(inputStream, player);
|
boolean invalidInjector = injector != null ? !injector.canInject(phase) : false;
|
||||||
break;
|
|
||||||
|
// Don't inject if the class has closed
|
||||||
|
if (!hasClosed && player != null && (tempHook != getInjectorType(injector) || invalidInjector)) {
|
||||||
} catch (PlayerLoggedOutException e) {
|
while (tempHook != PlayerInjectHooks.NONE) {
|
||||||
throw e;
|
// Whether or not the current hook method failed completely
|
||||||
|
boolean hookFailed = false;
|
||||||
} catch (Exception e) {
|
|
||||||
|
// Remove the previous hook, if any
|
||||||
// Mark this injection attempt as a failure
|
cleanupHook(injector);
|
||||||
logger.log(Level.SEVERE, "Player hook " + currentHook.toString() + " failed.", e);
|
|
||||||
|
try {
|
||||||
// Clean up as much as possible
|
injector = getHookInstance(player, tempHook);
|
||||||
try {
|
|
||||||
if (injector != null)
|
// Make sure this injection method supports the current game phase
|
||||||
injector.cleanupAll();
|
if (injector.canInject(phase)) {
|
||||||
} catch (Exception e2) {
|
injector.initialize();
|
||||||
logger.log(Level.WARNING, "Cleaing up after player hook failed.", e);
|
injector.injectManager();
|
||||||
}
|
|
||||||
|
DataInputStream inputStream = injector.getInputStream(false);
|
||||||
if (currentHook.ordinal() > 0) {
|
|
||||||
|
if (!player.isOnline() || inputStream == null) {
|
||||||
// Choose the previous player hook type
|
throw new PlayerLoggedOutException();
|
||||||
currentHook = PlayerInjectHooks.values()[currentHook.ordinal() - 1];
|
}
|
||||||
logger.log(Level.INFO, "Switching to " + currentHook.toString() + " instead.");
|
|
||||||
} else {
|
connectionLookup.put(inputStream, player);
|
||||||
// UTTER FAILURE
|
break;
|
||||||
playerInjection.put(player, null);
|
}
|
||||||
return;
|
|
||||||
}
|
} catch (PlayerLoggedOutException e) {
|
||||||
}
|
throw e;
|
||||||
}
|
|
||||||
|
} catch (Exception e) {
|
||||||
// Update values
|
// Mark this injection attempt as a failure
|
||||||
if (injector != null)
|
logger.log(Level.SEVERE, "Player hook " + tempHook.toString() + " failed.", e);
|
||||||
lastSuccessfulHook = injector;
|
hookFailed = true;
|
||||||
if (currentHook != playerHook || firstPlayer)
|
}
|
||||||
setPlayerHook(currentHook);
|
|
||||||
}
|
// Choose the previous player hook type
|
||||||
}
|
tempHook = PlayerInjectHooks.values()[tempHook.ordinal() - 1];
|
||||||
|
logger.log(Level.INFO, "Switching to " + tempHook.toString() + " instead.");
|
||||||
/**
|
|
||||||
* Unregisters the given player.
|
// Check for UTTER FAILURE
|
||||||
* @param player - player to unregister.
|
if (tempHook == PlayerInjectHooks.NONE) {
|
||||||
*/
|
cleanupHook(injector);
|
||||||
public void uninjectPlayer(Player player) {
|
injector = null;
|
||||||
if (!hasClosed && player != null) {
|
hookFailed = true;
|
||||||
|
}
|
||||||
PlayerInjector injector = playerInjection.get(player);
|
|
||||||
|
// Should we set the default hook method too?
|
||||||
if (injector != null) {
|
if (hookFailed) {
|
||||||
DataInputStream input = injector.getInputStream(true);
|
permanentHook = tempHook;
|
||||||
injector.cleanupAll();
|
}
|
||||||
|
}
|
||||||
playerInjection.remove(player);
|
|
||||||
connectionLookup.remove(input);
|
// Update values
|
||||||
}
|
if (injector != null)
|
||||||
}
|
lastSuccessfulHook = injector;
|
||||||
}
|
if (permanentHook != playerHook)
|
||||||
|
setPlayerHook(tempHook);
|
||||||
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
|
||||||
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
|
// Save last injector
|
||||||
}
|
playerInjection.put(player, injector);
|
||||||
|
}
|
||||||
private PlayerInjector getInjector(Player player) {
|
}
|
||||||
if (!playerInjection.containsKey(player)) {
|
|
||||||
// What? Try to inject again.
|
private void cleanupHook(PlayerInjector injector) {
|
||||||
injectPlayer(player);
|
// Clean up as much as possible
|
||||||
}
|
try {
|
||||||
|
if (injector != null)
|
||||||
PlayerInjector injector = playerInjection.get(player);
|
injector.cleanupAll();
|
||||||
|
} catch (Exception e2) {
|
||||||
// Check that the injector was sucessfully added
|
logger.log(Level.WARNING, "Cleaing up after player hook failed.", e2);
|
||||||
if (injector != null)
|
}
|
||||||
return injector;
|
}
|
||||||
else
|
|
||||||
throw new IllegalArgumentException("Player has no injected handler.");
|
/**
|
||||||
}
|
* Unregisters the given player.
|
||||||
|
* @param player - player to unregister.
|
||||||
/**
|
*/
|
||||||
* Determine if the given listeners are valid.
|
public void uninjectPlayer(Player player) {
|
||||||
* @param listeners - listeners to check.
|
if (!hasClosed && player != null) {
|
||||||
*/
|
|
||||||
public void checkListener(Set<PacketListener> listeners) {
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
// Make sure the current listeners are compatible
|
|
||||||
if (lastSuccessfulHook != null) {
|
if (injector != null) {
|
||||||
for (PacketListener listener : listeners) {
|
DataInputStream input = injector.getInputStream(true);
|
||||||
try {
|
injector.cleanupAll();
|
||||||
checkListener(listener);
|
|
||||||
} catch (IllegalStateException e) {
|
playerInjection.remove(player);
|
||||||
logger.log(Level.WARNING, "Unsupported listener.", e);
|
connectionLookup.remove(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void sendServerPacket(Player reciever, PacketContainer packet, boolean filters) throws InvocationTargetException {
|
||||||
/**
|
getInjector(reciever).sendServerPacket(packet.getHandle(), filters);
|
||||||
* Determine if a listener is valid or not.
|
}
|
||||||
* @param listener - listener to check.
|
|
||||||
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
|
private PlayerInjector getInjector(Player player) {
|
||||||
*/
|
if (!playerInjection.containsKey(player)) {
|
||||||
public void checkListener(PacketListener listener) {
|
// What? Try to inject again.
|
||||||
try {
|
injectPlayer(player);
|
||||||
if (lastSuccessfulHook != null)
|
}
|
||||||
lastSuccessfulHook.checkListener(listener);
|
|
||||||
} catch (Exception e) {
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
|
|
||||||
}
|
// Check that the injector was sucessfully added
|
||||||
}
|
if (injector != null)
|
||||||
|
return injector;
|
||||||
/**
|
else
|
||||||
* Process a packet as if it were sent by the given player.
|
throw new IllegalArgumentException("Player has no injected handler.");
|
||||||
* @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.
|
* Determine if the given listeners are valid.
|
||||||
*/
|
* @param listeners - listeners to check.
|
||||||
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException {
|
*/
|
||||||
|
public void checkListener(Set<PacketListener> listeners) {
|
||||||
PlayerInjector injector = getInjector(player);
|
// Make sure the current listeners are compatible
|
||||||
injector.processPacket(mcPacket);
|
if (lastSuccessfulHook != null) {
|
||||||
}
|
for (PacketListener listener : listeners) {
|
||||||
|
try {
|
||||||
/**
|
checkListener(listener);
|
||||||
* Retrieve the current list of registered sending listeners.
|
} catch (IllegalStateException e) {
|
||||||
* @return List of the sending listeners's packet IDs.
|
logger.log(Level.WARNING, "Unsupported listener.", e);
|
||||||
*/
|
}
|
||||||
public Set<Integer> getSendingFilters() {
|
}
|
||||||
return ImmutableSet.copyOf(sendingFilters);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the current logger.
|
* Determine if a listener is valid or not.
|
||||||
* @return Error logger.
|
* @param listener - listener to check.
|
||||||
*/
|
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
|
||||||
public Logger getLogger() {
|
*/
|
||||||
return logger;
|
public void checkListener(PacketListener listener) {
|
||||||
}
|
try {
|
||||||
|
if (lastSuccessfulHook != null)
|
||||||
public void close() {
|
lastSuccessfulHook.checkListener(listener);
|
||||||
|
} catch (Exception e) {
|
||||||
// Guard
|
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
|
||||||
if (hasClosed || playerInjection == null)
|
}
|
||||||
return;
|
}
|
||||||
|
|
||||||
// Remove everything
|
/**
|
||||||
for (PlayerInjector injection : playerInjection.values()) {
|
* Process a packet as if it were sent by the given player.
|
||||||
if (injection != null) {
|
* @param player - the sender.
|
||||||
injection.cleanupAll();
|
* @param mcPacket - the packet to process.
|
||||||
}
|
* @throws IllegalAccessException If the reflection machinery failed.
|
||||||
}
|
* @throws InvocationTargetException If the underlying method caused an error.
|
||||||
|
*/
|
||||||
// Remove server handler
|
public void processPacket(Player player, Packet mcPacket) throws IllegalAccessException, InvocationTargetException {
|
||||||
serverInjection.cleanupAll();
|
|
||||||
hasClosed = true;
|
PlayerInjector injector = getInjector(player);
|
||||||
|
injector.processPacket(mcPacket);
|
||||||
playerInjection.clear();
|
}
|
||||||
connectionLookup.clear();
|
|
||||||
invoker = null;
|
/**
|
||||||
}
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,340 +1,348 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* 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
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* 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;
|
* 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.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* 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;
|
* 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
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.injector.player;
|
package com.comphenix.protocol.injector.player;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
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.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import net.minecraft.server.EntityPlayer;
|
import net.minecraft.server.EntityPlayer;
|
||||||
import net.minecraft.server.Packet;
|
import net.minecraft.server.Packet;
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
|
|
||||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.events.PacketListener;
|
import com.comphenix.protocol.events.PacketListener;
|
||||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||||
import com.comphenix.protocol.reflect.StructureModifier;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.VolatileField;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.StructureModifier;
|
||||||
abstract class PlayerInjector {
|
import com.comphenix.protocol.reflect.VolatileField;
|
||||||
|
|
||||||
// Cache previously retrieved fields
|
abstract class PlayerInjector {
|
||||||
protected static Field serverHandlerField;
|
|
||||||
protected static Field proxyServerField;
|
// Cache previously retrieved fields
|
||||||
|
protected static Field serverHandlerField;
|
||||||
protected static Field networkManagerField;
|
protected static Field proxyServerField;
|
||||||
protected static Field inputField;
|
|
||||||
protected static Field netHandlerField;
|
protected static Field networkManagerField;
|
||||||
|
protected static Field inputField;
|
||||||
// Whether or not we're using a proxy type
|
protected static Field netHandlerField;
|
||||||
private static boolean hasProxyType;
|
|
||||||
|
// Whether or not we're using a proxy type
|
||||||
// To add our injected array lists
|
private static boolean hasProxyType;
|
||||||
protected static StructureModifier<Object> networkModifier;
|
|
||||||
|
// To add our injected array lists
|
||||||
// And methods
|
protected static StructureModifier<Object> networkModifier;
|
||||||
protected static Method queueMethod;
|
|
||||||
protected static Method processMethod;
|
// And methods
|
||||||
|
protected static Method queueMethod;
|
||||||
protected Player player;
|
protected static Method processMethod;
|
||||||
protected boolean hasInitialized;
|
|
||||||
|
protected Player player;
|
||||||
// Reference to the player's network manager
|
protected boolean hasInitialized;
|
||||||
protected VolatileField networkManagerRef;
|
|
||||||
protected VolatileField serverHandlerRef;
|
// Reference to the player's network manager
|
||||||
protected Object networkManager;
|
protected VolatileField networkManagerRef;
|
||||||
|
protected VolatileField serverHandlerRef;
|
||||||
// Current net handler
|
protected Object networkManager;
|
||||||
protected Object serverHandler;
|
|
||||||
protected Object netHandler;
|
// Current net handler
|
||||||
|
protected Object serverHandler;
|
||||||
// The packet manager and filters
|
protected Object netHandler;
|
||||||
protected ListenerInvoker invoker;
|
|
||||||
|
// The packet manager and filters
|
||||||
// Previous data input
|
protected ListenerInvoker invoker;
|
||||||
protected DataInputStream cachedInput;
|
|
||||||
|
// Previous data input
|
||||||
// Handle errors
|
protected DataInputStream cachedInput;
|
||||||
protected Logger logger;
|
|
||||||
|
// Handle errors
|
||||||
public PlayerInjector(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException {
|
protected Logger logger;
|
||||||
this.logger = logger;
|
|
||||||
this.player = player;
|
public PlayerInjector(Logger logger, Player player, ListenerInvoker invoker) throws IllegalAccessException {
|
||||||
this.invoker = invoker;
|
this.logger = logger;
|
||||||
}
|
this.player = player;
|
||||||
|
this.invoker = invoker;
|
||||||
/**
|
}
|
||||||
* Retrieve the notch (NMS) entity player object.
|
|
||||||
* @return Notch player object.
|
/**
|
||||||
*/
|
* Retrieve the notch (NMS) entity player object.
|
||||||
protected EntityPlayer getEntityPlayer() {
|
* @return Notch player object.
|
||||||
CraftPlayer craft = (CraftPlayer) player;
|
*/
|
||||||
return craft.getHandle();
|
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.
|
/**
|
||||||
*/
|
* Initialize all fields for this player injector, if it hasn't already.
|
||||||
public void initialize() throws IllegalAccessException {
|
* @throws IllegalAccessException An error has occured.
|
||||||
|
*/
|
||||||
EntityPlayer notchEntity = getEntityPlayer();
|
public void initialize() throws IllegalAccessException {
|
||||||
|
|
||||||
if (!hasInitialized) {
|
EntityPlayer notchEntity = getEntityPlayer();
|
||||||
// Do this first, in case we encounter an exception
|
|
||||||
hasInitialized = true;
|
if (!hasInitialized) {
|
||||||
|
// Do this first, in case we encounter an exception
|
||||||
// Retrieve the server handler
|
hasInitialized = true;
|
||||||
if (serverHandlerField == null) {
|
|
||||||
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler");
|
// Retrieve the server handler
|
||||||
proxyServerField = getProxyField(notchEntity, serverHandlerField);
|
if (serverHandlerField == null) {
|
||||||
}
|
serverHandlerField = FuzzyReflection.fromObject(notchEntity).getFieldByType(".*NetServerHandler");
|
||||||
|
proxyServerField = getProxyField(notchEntity, serverHandlerField);
|
||||||
// Yo dawg
|
}
|
||||||
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
|
|
||||||
serverHandler = serverHandlerRef.getValue();
|
// Yo dawg
|
||||||
|
serverHandlerRef = new VolatileField(serverHandlerField, notchEntity);
|
||||||
// Next, get the network manager
|
serverHandler = serverHandlerRef.getValue();
|
||||||
if (networkManagerField == null)
|
|
||||||
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
|
// Next, get the network manager
|
||||||
networkManagerRef = new VolatileField(networkManagerField, serverHandler);
|
if (networkManagerField == null)
|
||||||
networkManager = networkManagerRef.getValue();
|
networkManagerField = FuzzyReflection.fromObject(serverHandler).getFieldByType(".*NetworkManager");
|
||||||
|
networkManagerRef = new VolatileField(networkManagerField, serverHandler);
|
||||||
// Create the network manager modifier from the actual object type
|
networkManager = networkManagerRef.getValue();
|
||||||
if (networkManager != null && networkModifier == null)
|
|
||||||
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
|
// Create the network manager modifier from the actual object type
|
||||||
|
if (networkManager != null && networkModifier == null)
|
||||||
// And the queue method
|
networkModifier = new StructureModifier<Object>(networkManager.getClass(), null, false);
|
||||||
if (queueMethod == null)
|
|
||||||
queueMethod = FuzzyReflection.fromClass(networkManagerField.getType()).
|
// And the queue method
|
||||||
getMethodByParameters("queue", Packet.class );
|
if (queueMethod == null)
|
||||||
|
queueMethod = FuzzyReflection.fromClass(networkManagerField.getType()).
|
||||||
// And the data input stream that we'll use to identify a player
|
getMethodByParameters("queue", Packet.class );
|
||||||
if (inputField == null)
|
|
||||||
inputField = FuzzyReflection.fromObject(networkManager, true).
|
// And the data input stream that we'll use to identify a player
|
||||||
getFieldByType("java\\.io\\.DataInputStream");
|
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.
|
/**
|
||||||
*/
|
* Retrieve whether or not the server handler is a proxy object.
|
||||||
protected boolean hasProxyServerHandler() {
|
* @return TRUE if it is, FALSE otherwise.
|
||||||
return hasProxyType;
|
*/
|
||||||
}
|
protected boolean hasProxyServerHandler() {
|
||||||
|
return hasProxyType;
|
||||||
private Field getProxyField(EntityPlayer notchEntity, Field serverField) {
|
}
|
||||||
|
|
||||||
try {
|
private Field getProxyField(EntityPlayer notchEntity, Field serverField) {
|
||||||
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
|
||||||
|
try {
|
||||||
// Is this a Minecraft hook?
|
Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true);
|
||||||
if (handler != null && !handler.getClass().getName().startsWith("net.minecraft.server")) {
|
|
||||||
|
// Is this a Minecraft hook?
|
||||||
// This is our proxy object
|
if (handler != null && !handler.getClass().getName().startsWith("net.minecraft.server")) {
|
||||||
if (handler instanceof Factory)
|
|
||||||
return null;
|
// This is our proxy object
|
||||||
|
if (handler instanceof Factory)
|
||||||
hasProxyType = true;
|
return null;
|
||||||
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
|
|
||||||
|
hasProxyType = true;
|
||||||
// No? Is it a Proxy type?
|
logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!");
|
||||||
try {
|
|
||||||
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
|
// No? Is it a Proxy type?
|
||||||
|
try {
|
||||||
// It might be
|
FuzzyReflection reflection = FuzzyReflection.fromObject(handler, true);
|
||||||
return reflection.getFieldByType(".*NetServerHandler");
|
|
||||||
|
// It might be
|
||||||
} catch (RuntimeException e) {
|
return reflection.getFieldByType(".*NetServerHandler");
|
||||||
// Damn
|
|
||||||
}
|
} catch (RuntimeException e) {
|
||||||
}
|
// Damn
|
||||||
|
}
|
||||||
} catch (IllegalAccessException e) {
|
}
|
||||||
logger.warning("Unable to load server handler from proxy type.");
|
|
||||||
}
|
} catch (IllegalAccessException e) {
|
||||||
|
logger.warning("Unable to load server handler from proxy type.");
|
||||||
// Nope, just go with it
|
}
|
||||||
return null;
|
|
||||||
}
|
// 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.
|
* Retrieves the current net handler for this player.
|
||||||
*/
|
* @return Current net handler.
|
||||||
protected Object getNetHandler() throws IllegalAccessException {
|
* @throws IllegalAccessException Unable to find or retrieve net handler.
|
||||||
|
*/
|
||||||
// What a mess
|
protected Object getNetHandler() throws IllegalAccessException {
|
||||||
try {
|
|
||||||
if (netHandlerField == null)
|
// What a mess
|
||||||
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
try {
|
||||||
getFieldByType("net\\.minecraft\\.NetHandler");
|
if (netHandlerField == null)
|
||||||
} catch (RuntimeException e1) {
|
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
||||||
// Swallow it
|
getFieldByType("net\\.minecraft\\.NetHandler");
|
||||||
}
|
} catch (RuntimeException e1) {
|
||||||
|
// Swallow it
|
||||||
// Second attempt
|
}
|
||||||
if (netHandlerField == null) {
|
|
||||||
try {
|
// Second attempt
|
||||||
// Well, that sucks. Try just Minecraft objects then.
|
if (netHandlerField == null) {
|
||||||
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
try {
|
||||||
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
// Well, that sucks. Try just Minecraft objects then.
|
||||||
|
netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true).
|
||||||
} catch (RuntimeException e2) {
|
getFieldByType(FuzzyReflection.MINECRAFT_OBJECT);
|
||||||
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
|
|
||||||
}
|
} catch (RuntimeException e2) {
|
||||||
}
|
throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage());
|
||||||
|
}
|
||||||
// Get the handler
|
}
|
||||||
if (netHandler == null)
|
|
||||||
netHandler = FieldUtils.readField(netHandlerField, networkManager, true);
|
// Get the handler
|
||||||
return netHandler;
|
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.
|
* Processes the given packet as if it was transmitted by the current player.
|
||||||
* @throws InvocationTargetException If the underlying method caused an error.
|
* @param packet - packet to process.
|
||||||
*/
|
* @throws IllegalAccessException If the reflection machinery failed.
|
||||||
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException {
|
* @throws InvocationTargetException If the underlying method caused an error.
|
||||||
|
*/
|
||||||
Object netHandler = getNetHandler();
|
public void processPacket(Packet packet) throws IllegalAccessException, InvocationTargetException {
|
||||||
|
|
||||||
// Get the process method
|
Object netHandler = getNetHandler();
|
||||||
if (processMethod == null) {
|
|
||||||
try {
|
// Get the process method
|
||||||
processMethod = FuzzyReflection.fromClass(Packet.class).
|
if (processMethod == null) {
|
||||||
getMethodByParameters("processPacket", netHandlerField.getType());
|
try {
|
||||||
} catch (RuntimeException e) {
|
processMethod = FuzzyReflection.fromClass(Packet.class).
|
||||||
throw new IllegalArgumentException("Cannot locate process packet method: " + e.getMessage());
|
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);
|
// We're ready
|
||||||
} catch (IllegalArgumentException e) {
|
try {
|
||||||
throw new IllegalArgumentException("Method " + processMethod.getName() + " is not compatible.");
|
processMethod.invoke(packet, netHandler);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw 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.
|
* Send a packet to the client.
|
||||||
* @param InvocationTargetException If an error occured when sending the packet.
|
* @param packet - server packet to send.
|
||||||
*/
|
* @param filtered - whether or not the packet will be filtered by our listeners.
|
||||||
public abstract void sendServerPacket(Packet packet, boolean filtered) throws InvocationTargetException;
|
* @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();
|
* Inject a hook to catch packets sent to the current player.
|
||||||
|
*/
|
||||||
/**
|
public abstract void injectManager();
|
||||||
* Remove all hooks and modifications.
|
|
||||||
*/
|
/**
|
||||||
public abstract void cleanupAll();
|
* 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.
|
/**
|
||||||
*/
|
* Determine if this inject method can even be attempted.
|
||||||
public abstract boolean canInject();
|
* @return TRUE if can be attempted, though possibly with failure, FALSE otherwise.
|
||||||
|
*/
|
||||||
/**
|
public abstract boolean canInject(GamePhase state);
|
||||||
* 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.
|
* Retrieve the hook type this class represents.
|
||||||
* @param listener - the listener that is about to be registered.
|
* @return Hook type this class represents.
|
||||||
*/
|
*/
|
||||||
public abstract void checkListener(PacketListener listener);
|
public abstract PlayerInjectHooks getHookType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows a packet to be recieved by the listeners.
|
* Invoked before a new listener is registered.
|
||||||
* @param packet - packet to recieve.
|
* <p>
|
||||||
* @return The given packet, or the packet replaced by the listeners.
|
* 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 Packet handlePacketRecieved(Packet packet) {
|
*/
|
||||||
// Get the packet ID too
|
public abstract void checkListener(PacketListener listener);
|
||||||
Integer id = invoker.getPacketID(packet);
|
|
||||||
|
/**
|
||||||
// Make sure we're listening
|
* Allows a packet to be recieved by the listeners.
|
||||||
if (id != null && hasListener(id)) {
|
* @param packet - packet to recieve.
|
||||||
// A packet has been sent guys!
|
* @return The given packet, or the packet replaced by the listeners.
|
||||||
PacketContainer container = new PacketContainer(id, packet);
|
*/
|
||||||
PacketEvent event = PacketEvent.fromServer(invoker, container, player);
|
public Packet handlePacketRecieved(Packet packet) {
|
||||||
invoker.invokePacketSending(event);
|
// Get the packet ID too
|
||||||
|
Integer id = invoker.getPacketID(packet);
|
||||||
// Cancelling is pretty simple. Just ignore the packet.
|
|
||||||
if (event.isCancelled())
|
// Make sure we're listening
|
||||||
return null;
|
if (id != null && hasListener(id)) {
|
||||||
|
// A packet has been sent guys!
|
||||||
// Right, remember to replace the packet again
|
PacketContainer container = new PacketContainer(id, packet);
|
||||||
return event.getPacket().getHandle();
|
PacketEvent event = PacketEvent.fromServer(invoker, container, player);
|
||||||
}
|
invoker.invokePacketSending(event);
|
||||||
|
|
||||||
return packet;
|
// Cancelling is pretty simple. Just ignore the packet.
|
||||||
}
|
if (event.isCancelled())
|
||||||
|
return null;
|
||||||
/**
|
|
||||||
* Determine if the given injector is listening for this packet ID.
|
// Right, remember to replace the packet again
|
||||||
* @param packetID - packet ID to check.
|
return event.getPacket().getHandle();
|
||||||
* @return TRUE if it is, FALSE oterhwise.
|
}
|
||||||
*/
|
|
||||||
protected abstract boolean hasListener(int packetID);
|
return packet;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Retrieve the current player's input stream.
|
/**
|
||||||
* @param cache - whether or not to cache the result of this method.
|
* Determine if the given injector is listening for this packet ID.
|
||||||
* @return The player's input stream.
|
* @param packetID - packet ID to check.
|
||||||
*/
|
* @return TRUE if it is, FALSE oterhwise.
|
||||||
public DataInputStream getInputStream(boolean cache) {
|
*/
|
||||||
if (inputField == null)
|
protected abstract boolean hasListener(int packetID);
|
||||||
throw new IllegalStateException("Input field is NULL.");
|
|
||||||
if (networkManager == null)
|
/**
|
||||||
throw new IllegalStateException("Network manager is NULL.");
|
* Retrieve the current player's input stream.
|
||||||
|
* @param cache - whether or not to cache the result of this method.
|
||||||
// Get the associated input stream
|
* @return The player's input stream.
|
||||||
try {
|
*/
|
||||||
if (cache && cachedInput != null)
|
public DataInputStream getInputStream(boolean cache) {
|
||||||
return cachedInput;
|
if (inputField == null)
|
||||||
|
throw new IllegalStateException("Input field is NULL.");
|
||||||
// Save to cache
|
if (networkManager == null)
|
||||||
cachedInput = (DataInputStream) FieldUtils.readField(inputField, networkManager, true);
|
throw new IllegalStateException("Network manager is NULL.");
|
||||||
return cachedInput;
|
|
||||||
|
// Get the associated input stream
|
||||||
} catch (IllegalAccessException e) {
|
try {
|
||||||
throw new RuntimeException("Unable to read input stream.", e);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren