Archiviert
13
0

Minimize the number of unnecessary references.

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-06-19 02:07:12 +02:00
Ursprung e7954a0f79
Commit 8f2935e241
3 geänderte Dateien mit 414 neuen und 391 gelöschten Zeilen

Datei anzeigen

@ -285,43 +285,11 @@ public class PacketContainer implements Serializable {
* internal Minecraft ItemStack. * internal Minecraft ItemStack.
* @return A modifier for ItemStack array fields. * @return A modifier for ItemStack array fields.
*/ */
public StructureModifier<ItemStack[]> getItemArrayModifier() { public StructureModifier<ItemStack[]> getItemArrayModifier() {
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
// Convert to and from the Bukkit wrapper // Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack[]>withType( return structureModifier.<ItemStack[]>withType(
MinecraftReflection.getItemStackArrayClass(), MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new EquivalentConverter<ItemStack[]>() { BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
}
return result;
}
@Override
public ItemStack[] getSpecific(Object generic) {
Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length];
// Add the wrapper
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getSpecific(input[i]);
}
return result;
}
@Override
public Class<ItemStack[]> getSpecificType() {
return ItemStack[].class;
}
}));
} }
/** /**
@ -556,4 +524,40 @@ public class PacketContainer implements Serializable {
return method; return method;
} }
/**
* Represents an equivalent converter for ItemStack arrays.
* @author Kristian
*/
private static class ItemStackArrayConverter implements EquivalentConverter<ItemStack[]> {
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
}
return result;
}
@Override
public ItemStack[] getSpecific(Object generic) {
Object[] input = (Object[]) generic;
ItemStack[] result = new ItemStack[input.length];
// Add the wrapper
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getSpecific(input[i]);
}
return result;
}
@Override
public Class<ItemStack[]> getSpecificType() {
return ItemStack[].class;
}
}
} }

Datei anzeigen

@ -1,352 +1,358 @@
/* /*
* 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.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import net.sf.cglib.proxy.*; import net.sf.cglib.proxy.*;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.concurrency.IntegerSet; import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.VolatileField;
import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.ExistingGenerator; import com.comphenix.protocol.reflect.instances.ExistingGenerator;
import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
/** /**
* Represents a player hook into the NetServerHandler class. * Represents a player hook into the NetServerHandler class.
* *
* @author Kristian * @author Kristian
*/ */
class NetworkServerInjector extends PlayerInjector { class NetworkServerInjector extends PlayerInjector {
// Disconnected field // Disconnected field
public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s."); public static final ReportType REPORT_ASSUMING_DISCONNECT_FIELD = new ReportType("Unable to find 'disconnected' field. Assuming %s.");
public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?"); public static final ReportType REPORT_DISCONNECT_FIELD_MISSING = new ReportType("Cannot find disconnected field. Is ProtocolLib up to date?");
public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice."); public static final ReportType REPORT_DISCONNECT_FIELD_FAILURE = new ReportType("Unable to update disconnected field. Player quit event may be sent twice.");
private volatile static CallbackFilter callbackFilter; private volatile static CallbackFilter callbackFilter;
private volatile static boolean foundSendPacket; private volatile static boolean foundSendPacket;
private volatile static Field disconnectField; private volatile static Field disconnectField;
private InjectedServerConnection serverInjection; private InjectedServerConnection serverInjection;
// Determine if we're listening // Determine if we're listening
private IntegerSet sendingFilters; private IntegerSet sendingFilters;
// Used to create proxy objects // Used to create proxy objects
private ClassLoader classLoader; private ClassLoader classLoader;
// Whether or not the player has disconnected // Whether or not the player has disconnected
private boolean hasDisconnected; private boolean hasDisconnected;
// Used to copy fields // Used to copy fields
private final ObjectWriter writer = new ObjectWriter(); private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector( public NetworkServerInjector(
ClassLoader classLoader, ErrorReporter reporter, Player player, ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters, ListenerInvoker invoker, IntegerSet sendingFilters,
InjectedServerConnection serverInjection) throws IllegalAccessException { InjectedServerConnection serverInjection) throws IllegalAccessException {
super(reporter, player, invoker); super(reporter, player, invoker);
this.classLoader = classLoader; this.classLoader = classLoader;
this.sendingFilters = sendingFilters; this.sendingFilters = sendingFilters;
this.serverInjection = serverInjection; this.serverInjection = serverInjection;
} }
@Override @Override
protected boolean hasListener(int packetID) { protected boolean hasListener(int packetID) {
return sendingFilters.contains(packetID); return sendingFilters.contains(packetID);
} }
@Override @Override
public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException {
Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue();
if (serverDelegate != null) { if (serverDelegate != null) {
try { try {
// Note that invocation target exception is a wrapper for a checked exception // Note that invocation target exception is a wrapper for a checked exception
MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet); MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw e; throw e;
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw e; throw e;
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access send packet method.", e); throw new IllegalStateException("Unable to access send packet method.", e);
} }
} else { } else {
throw new IllegalStateException("Unable to load server handler. Cannot send packet."); throw new IllegalStateException("Unable to load server handler. Cannot send packet.");
} }
} }
@Override @Override
public void injectManager() { public void injectManager() {
if (serverHandlerRef == null) if (serverHandlerRef == null)
throw new IllegalStateException("Cannot find server handler."); throw new IllegalStateException("Cannot find server handler.");
// Don't inject twice // Don't inject twice
if (serverHandlerRef.getValue() instanceof Factory) if (serverHandlerRef.getValue() instanceof Factory)
return; return;
if (!tryInjectManager()) { if (!tryInjectManager()) {
Class<?> serverHandlerClass = MinecraftReflection.getNetServerHandlerClass(); Class<?> serverHandlerClass = MinecraftReflection.getNetServerHandlerClass();
// Try to override the proxied object // Try to override the proxied object
if (proxyServerField != null) { if (proxyServerField != null) {
serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true); serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true);
serverHandler = serverHandlerRef.getValue(); serverHandler = serverHandlerRef.getValue();
if (serverHandler == null) if (serverHandler == null)
throw new RuntimeException("Cannot hook player: Inner proxy object is NULL."); throw new RuntimeException("Cannot hook player: Inner proxy object is NULL.");
else else
serverHandlerClass = serverHandler.getClass(); serverHandlerClass = serverHandler.getClass();
// Try again // Try again
if (tryInjectManager()) { if (tryInjectManager()) {
// It worked - probably // It worked - probably
return; return;
} }
} }
throw new RuntimeException( throw new RuntimeException(
"Cannot hook player: Unable to find a valid constructor for the " "Cannot hook player: Unable to find a valid constructor for the "
+ serverHandlerClass.getName() + " object."); + serverHandlerClass.getName() + " object.");
} }
} }
private boolean tryInjectManager() { private boolean tryInjectManager() {
Class<?> serverClass = serverHandler.getClass(); Class<?> serverClass = serverHandler.getClass();
Enhancer ex = new Enhancer(); Enhancer ex = new Enhancer();
Callback sendPacketCallback = new MethodInterceptor() { Callback sendPacketCallback = new MethodInterceptor() {
@Override @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object packet = args[0]; Object packet = args[0];
if (packet != null) { if (packet != null) {
packet = handlePacketSending(packet); packet = handlePacketSending(packet);
// A NULL packet indicate cancelling // A NULL packet indicate cancelling
if (packet != null) if (packet != null)
args[0] = packet; args[0] = packet;
else else
return null; return null;
} }
// Call the method directly // Call the method directly
return proxy.invokeSuper(obj, args); return proxy.invokeSuper(obj, args);
}; };
}; };
Callback noOpCallback = NoOp.INSTANCE; Callback noOpCallback = NoOp.INSTANCE;
// Share callback filter - that way, we avoid generating a new class for // Share callback filter - that way, we avoid generating a new class for
// every logged in player. // every logged in player.
if (callbackFilter == null) { if (callbackFilter == null) {
final Method sendPacket = MinecraftMethods.getSendPacketMethod(); callbackFilter = new SendMethodFilter();
}
callbackFilter = new CallbackFilter() {
@Override ex.setClassLoader(classLoader);
public int accept(Method method) { ex.setSuperclass(serverClass);
if (isCallableEqual(sendPacket, method)) { ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback });
foundSendPacket = true; ex.setCallbackFilter(callbackFilter);
return 0;
} else { // Find the Minecraft NetServerHandler superclass
return 1; Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass());
} ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass);
} DefaultInstances serverInstances = null;
};
} // Maybe the proxy instance can help?
Object proxyInstance = getProxyServerHandler();
ex.setClassLoader(classLoader);
ex.setSuperclass(serverClass); // Use the existing server proxy when we create one
ex.setCallbacks(new Callback[] { sendPacketCallback, noOpCallback }); if (proxyInstance != null && proxyInstance != serverHandler) {
ex.setCallbackFilter(callbackFilter); serverInstances = DefaultInstances.fromArray(generator,
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance }));
// Find the Minecraft NetServerHandler superclass } else {
Class<?> minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass()); serverInstances = DefaultInstances.fromArray(generator);
ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass); }
DefaultInstances serverInstances = null;
serverInstances.setNonNull(true);
// Maybe the proxy instance can help? serverInstances.setMaximumRecursion(1);
Object proxyInstance = getProxyServerHandler();
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass);
// Use the existing server proxy when we create one
if (proxyInstance != null && proxyInstance != serverHandler) { // Inject it now
serverInstances = DefaultInstances.fromArray(generator, if (proxyObject != null) {
ExistingGenerator.fromObjectArray(new Object[] { proxyInstance })); // Did we override a sendPacket method?
} else { if (!foundSendPacket) {
serverInstances = DefaultInstances.fromArray(generator); throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass);
} }
serverInstances.setNonNull(true); serverInjection.replaceServerHandler(serverHandler, proxyObject);
serverInstances.setMaximumRecursion(1); serverHandlerRef.setValue(proxyObject);
return true;
Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass); } else {
return false;
// Inject it now }
if (proxyObject != null) { }
// Did we override a sendPacket method?
if (!foundSendPacket) { private Object getProxyServerHandler() {
throw new IllegalArgumentException("Unable to find a sendPacket method in " + serverClass); if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) {
} try {
return FieldUtils.readField(proxyServerField, serverHandler, true);
serverInjection.replaceServerHandler(serverHandler, proxyObject); } catch (Throwable e) {
serverHandlerRef.setValue(proxyObject); // Oh well
return true; }
} else { }
return false;
} return null;
} }
/** private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) {
* Determine if the two methods are equal in terms of call semantics. if (MinecraftReflection.isMinecraftClass(clazz))
* <p> return clazz;
* Two methods are equal if they have the same name, parameter types and return type. else if (clazz.equals(Object.class))
* @param first - first method. return clazz;
* @param second - second method. else
* @return TRUE if they are, FALSE otherwise. return getFirstMinecraftSuperClass(clazz.getSuperclass());
*/ }
private boolean isCallableEqual(Method first, Method second) {
return first.getName().equals(second.getName()) && @Override
first.getReturnType().equals(second.getReturnType()) && protected void cleanHook() {
Arrays.equals(first.getParameterTypes(), second.getParameterTypes()); if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
} writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
private Object getProxyServerHandler() {
if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) { try {
try { if (getNetHandler() != null) {
return FieldUtils.readField(proxyServerField, serverHandler, true); // Restore packet listener
} catch (Throwable e) { try {
// Oh well FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true);
} } catch (IllegalAccessException e) {
} // Oh well
e.printStackTrace();
return null; }
} }
} catch (IllegalAccessException e) {
private Class<?> getFirstMinecraftSuperClass(Class<?> clazz) { e.printStackTrace();
if (MinecraftReflection.isMinecraftClass(clazz)) }
return clazz;
else if (clazz.equals(Object.class)) // Prevent the PlayerQuitEvent from being sent twice
return clazz; if (hasDisconnected) {
else setDisconnect(serverHandlerRef.getValue(), true);
return getFirstMinecraftSuperClass(clazz.getSuperclass()); }
} }
@Override serverInjection.revertServerHandler(serverHandler);
protected void cleanHook() { }
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass()); @Override
serverHandlerRef.revertValue(); public void handleDisconnect() {
hasDisconnected = true;
try { }
if (getNetHandler() != null) {
// Restore packet listener /**
try { * Set the disconnected field in a NetServerHandler.
FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true); * @param handler - the NetServerHandler.
} catch (IllegalAccessException e) { * @param value - the new value.
// Oh well */
e.printStackTrace(); private void setDisconnect(Object handler, boolean value) {
} // Set it
} try {
} catch (IllegalAccessException e) { // Load the field
e.printStackTrace(); if (disconnectField == null) {
} disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
}
// Prevent the PlayerQuitEvent from being sent twice FieldUtils.writeField(disconnectField, handler, value);
if (hasDisconnected) {
setDisconnect(serverHandlerRef.getValue(), true); } catch (IllegalArgumentException e) {
} // Assume it's the first ...
} if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class);
serverInjection.revertServerHandler(serverHandler); reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField));
}
// Try again
@Override if (disconnectField != null) {
public void handleDisconnect() { setDisconnect(handler, value);
hasDisconnected = true; return;
} }
}
/**
* Set the disconnected field in a NetServerHandler. // This is really bad
* @param handler - the NetServerHandler. reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e));
* @param value - the new value.
*/ } catch (IllegalAccessException e) {
private void setDisconnect(Object handler, boolean value) { reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e));
// Set it }
try { }
// Load the field
if (disconnectField == null) { @Override
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*"); public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) {
} // We support everything
FieldUtils.writeField(disconnectField, handler, value); return null;
}
} catch (IllegalArgumentException e) {
// Assume it's the first ... @Override
if (disconnectField == null) { public boolean canInject(GamePhase phase) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByType("disconnected", boolean.class); // Doesn't work when logging in
reporter.reportWarning(this, Report.newBuilder(REPORT_ASSUMING_DISCONNECT_FIELD).messageParam(disconnectField)); return phase == GamePhase.PLAYING;
}
// Try again
if (disconnectField != null) { @Override
setDisconnect(handler, value); public PlayerInjectHooks getHookType() {
return; return PlayerInjectHooks.NETWORK_SERVER_OBJECT;
} }
}
/**
// This is really bad * Represents a CallbackFilter that only matches the SendPacket method.
reporter.reportDetailed(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_MISSING).error(e)); * @author Kristian
*/
} catch (IllegalAccessException e) { private static class SendMethodFilter implements CallbackFilter {
reporter.reportWarning(this, Report.newBuilder(REPORT_DISCONNECT_FIELD_FAILURE).error(e)); private Method sendPacket = MinecraftMethods.getSendPacketMethod();
}
} @Override
public int accept(Method method) {
@Override if (isCallableEqual(sendPacket, method)) {
public UnsupportedListener checkListener(MinecraftVersion version, PacketListener listener) { NetworkServerInjector.foundSendPacket = true;
// We support everything return 0;
return null; } else {
} return 1;
}
@Override }
public boolean canInject(GamePhase phase) {
// Doesn't work when logging in /**
return phase == GamePhase.PLAYING; * Determine if the two methods are equal in terms of call semantics.
} * <p>
* Two methods are equal if they have the same name, parameter types and return type.
@Override * @param first - first method.
public PlayerInjectHooks getHookType() { * @param second - second method.
return PlayerInjectHooks.NETWORK_SERVER_OBJECT; * @return TRUE if they are, FALSE otherwise.
} */
} private boolean isCallableEqual(Method first, Method second) {
return first.getName().equals(second.getName()) &&
first.getReturnType().equals(second.getReturnType()) &&
Arrays.equals(first.getParameterTypes(), second.getParameterTypes());
}
}
}

Datei anzeigen

@ -18,6 +18,7 @@
package com.comphenix.protocol.injector.player; package com.comphenix.protocol.injector.player;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -81,7 +82,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
private NetLoginInjector netLoginInjector; private NetLoginInjector netLoginInjector;
// The last successful player hook // The last successful player hook
private PlayerInjector lastSuccessfulHook; private WeakReference<PlayerInjector> lastSuccessfulHook;
// Dummy injection // Dummy injection
private Cache<Player, PlayerInjector> dummyInjectors = private Cache<Player, PlayerInjector> dummyInjectors =
@ -399,7 +400,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
// Update values // Update values
if (injector != null) if (injector != null)
lastSuccessfulHook = injector; lastSuccessfulHook = new WeakReference<PlayerInjector>(injector);
if (permanentHook != getPlayerHook(phase)) if (permanentHook != getPlayerHook(phase))
setPlayerHook(phase, tempHook); setPlayerHook(phase, tempHook);
@ -649,13 +650,23 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
@Override @Override
public void checkListener(Set<PacketListener> listeners) { public void checkListener(Set<PacketListener> listeners) {
// Make sure the current listeners are compatible // Make sure the current listeners are compatible
if (lastSuccessfulHook != null) { if (getLastSuccessfulHook() != null) {
for (PacketListener listener : listeners) { for (PacketListener listener : listeners) {
checkListener(listener); checkListener(listener);
} }
} }
} }
/**
* Retrieve the last successful hook.
* <p>
* May be NULL if the hook has been uninjected.
* @return Last successful hook.
*/
private PlayerInjector getLastSuccessfulHook() {
return lastSuccessfulHook != null ? lastSuccessfulHook.get() : null;
}
/** /**
* Determine if a listener is valid or not. * Determine if a listener is valid or not.
* <p> * <p>
@ -664,8 +675,10 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
*/ */
@Override @Override
public void checkListener(PacketListener listener) { public void checkListener(PacketListener listener) {
if (lastSuccessfulHook != null) { PlayerInjector last = getLastSuccessfulHook();
UnsupportedListener result = lastSuccessfulHook.checkListener(version, listener);
if (last != null) {
UnsupportedListener result = last.checkListener(version, listener);
// We won't prevent the listener, as it may still have valid packets // We won't prevent the listener, as it may still have valid packets
if (result != null) { if (result != null) {