Prevent the PlayerQuitEvent from being fired twice. FIXES Ticket-2.

This commit is contained in:
Kristian S. Stangeland 2012-10-23 00:37:35 +02:00
parent 0aac8ebf0a
commit 96ad954cf7
9 changed files with 105 additions and 8 deletions

View File

@ -4,7 +4,7 @@
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name> <name>ProtocolLib</name>
<version>1.4.2</version> <version>1.4.3-SNAPSHOT</version>
<description>Provides read/write access to the Minecraft protocol.</description> <description>Provides read/write access to the Minecraft protocol.</description>
<url>http://dev.bukkit.org/server-mods/protocollib/</url> <url>http://dev.bukkit.org/server-mods/protocollib/</url>
<developers> <developers>

View File

@ -606,6 +606,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
playerInjection.handleDisconnect(event.getPlayer());
playerInjection.uninjectPlayer(event.getPlayer()); playerInjection.uninjectPlayer(event.getPlayer());
} }
@ -689,10 +690,14 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
Object event = args[0]; Object event = args[0];
// Check for the correct event // Check for the correct event
if (event instanceof PlayerJoinEvent) if (event instanceof PlayerJoinEvent) {
playerInjection.injectPlayer(((PlayerJoinEvent) event).getPlayer()); Player player = ((PlayerJoinEvent) event).getPlayer();
else if (event instanceof PlayerQuitEvent) playerInjection.injectPlayer(player);
playerInjection.uninjectPlayer(((PlayerQuitEvent) event).getPlayer()); } else if (event instanceof PlayerQuitEvent) {
Player player = ((PlayerQuitEvent) event).getPlayer();
playerInjection.handleDisconnect(player);
playerInjection.uninjectPlayer(player);
}
} }
return null; return null;
} }

View File

@ -191,6 +191,11 @@ class NetworkFieldInjector extends PlayerInjector {
overridenLists.clear(); overridenLists.clear();
} }
@Override
public void handleDisconnect() {
// No need to do anything
}
@Override @Override
public boolean canInject(GamePhase phase) { public boolean canInject(GamePhase phase) {
// All phases should work // All phases should work

View File

@ -159,6 +159,11 @@ class NetworkObjectInjector extends PlayerInjector {
} }
} }
@Override
public void handleDisconnect() {
// No need to do anything
}
@Override @Override
public boolean canInject(GamePhase phase) { public boolean canInject(GamePhase phase) {
// Works for all phases // Works for all phases

View File

@ -17,8 +17,10 @@
package com.comphenix.protocol.injector.player; package com.comphenix.protocol.injector.player;
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.Logger; import java.util.logging.Logger;
import net.minecraft.server.Packet; import net.minecraft.server.Packet;
@ -50,6 +52,7 @@ import com.comphenix.protocol.reflect.instances.ExistingGenerator;
*/ */
public class NetworkServerInjector extends PlayerInjector { public class NetworkServerInjector extends PlayerInjector {
private static Field disconnectField;
private static Method sendPacketMethod; private static Method sendPacketMethod;
private InjectedServerConnection serverInjection; private InjectedServerConnection serverInjection;
@ -59,6 +62,9 @@ public class NetworkServerInjector extends PlayerInjector {
// Used to create proxy objects // Used to create proxy objects
private ClassLoader classLoader; private ClassLoader classLoader;
// Whether or not the player has disconnected
private boolean hasDisconnected;
public NetworkServerInjector( public NetworkServerInjector(
ClassLoader classLoader, Logger logger, Player player, ClassLoader classLoader, Logger logger, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters, ListenerInvoker invoker, IntegerSet sendingFilters,
@ -250,11 +256,42 @@ public class NetworkServerInjector extends PlayerInjector {
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
e.printStackTrace(); e.printStackTrace();
} }
// Prevent the PlayerQuitEvent from being sent twice
if (hasDisconnected) {
setDisconnect(serverHandlerRef.getValue(), true);
}
} }
serverInjection.revertServerHandler(serverHandler); serverInjection.revertServerHandler(serverHandler);
} }
@Override
public void handleDisconnect() {
hasDisconnected = true;
}
/**
* Set the disconnected field in a NetServerHandler.
* @param handler - the NetServerHandler.
* @param value - the new value.
*/
private void setDisconnect(Object handler, boolean value) {
// Set it
try {
// Load the field
if (disconnectField == null) {
disconnectField = FuzzyReflection.fromObject(handler).getFieldByName("disconnected.*");
}
FieldUtils.writeField(disconnectField, handler, value);
} catch (IllegalArgumentException e) {
logger.log(Level.WARNING, "Unable to find disconnect field. Is ProtocolLib up to date?");
} catch (IllegalAccessException e) {
logger.log(Level.WARNING, "Unable to update disconnected field. Player quit event may be sent twice.");
}
}
@Override @Override
public void checkListener(PacketListener listener) { public void checkListener(PacketListener listener) {
// We support everything // We support everything

View File

@ -358,6 +358,18 @@ public class PlayerInjectionHandler {
} }
} }
/**
* Invoke special routines for handling disconnect before a player is uninjected.
* @param player - player to process.
*/
public void handleDisconnect(Player player) {
PlayerInjector injector = getInjector(player);
if (injector != null) {
injector.handleDisconnect();
}
}
/** /**
* Unregisters the given player. * Unregisters the given player.
* @param player - player to unregister. * @param player - player to unregister.

View File

@ -452,6 +452,11 @@ abstract class PlayerInjector {
clean = true; clean = true;
} }
/**
* Clean up after the player has disconnected.
*/
public abstract void handleDisconnect();
/** /**
* Override to add custom cleanup behavior. * Override to add custom cleanup behavior.
*/ */

View File

@ -68,7 +68,7 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
} }
/** /**
* Invoksed when an element is being removed. * Invoked when an element is being removed.
* @param removing - the element being removed. * @param removing - the element being removed.
*/ */
protected void onRemoved(TKey removing) { protected void onRemoved(TKey removing) {
@ -264,6 +264,15 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
addMapping(target, replacement, false); addMapping(target, replacement, false);
} }
/**
* Retrieve the old value, if it exists.
* @param target - the key.
* @return The value that was replaced, or NULL.
*/
public TKey getMapping(TKey target) {
return replaceMap.get(target);
}
/** /**
* Add a replace rule. * Add a replace rule.
* <p> * <p>
@ -284,8 +293,9 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
/** /**
* Revert the given mapping. * Revert the given mapping.
* @param target - the instance we replaced. * @param target - the instance we replaced.
* @return The old mapped value, or NULL if nothing was replaced.
*/ */
public synchronized void removeMapping(TKey target) { public synchronized TKey removeMapping(TKey target) {
// Make sure the mapping exist // Make sure the mapping exist
if (replaceMap.containsKey(target)) { if (replaceMap.containsKey(target)) {
TKey replacement = replaceMap.get(target); TKey replacement = replaceMap.get(target);
@ -293,7 +303,25 @@ class ReplacedArrayList<TKey> extends ArrayList<TKey> {
// Revert existing elements // Revert existing elements
replaceAll(replacement, target); replaceAll(replacement, target);
return replacement;
} }
return null;
}
/**
* Swap the new replaced value with its old value.
* @param target - the instance we replaced.
* @param The old mapped value, or NULL if nothing was swapped.
*/
public synchronized TKey swapMapping(TKey target) {
// Make sure the mapping exist
TKey replacement = removeMapping(target);
// Add the reverse
if (replacement != null) {
replaceMap.put(replacement, target);
}
return replacement;
} }
/** /**