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> <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 anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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