Ensure that server operators are informed about incompatibility.

This commit is contained in:
Kristian S. Stangeland 2012-09-26 06:00:26 +02:00
parent 9efb85e7c3
commit 90970d1b9b
6 changed files with 110 additions and 34 deletions

View File

@ -86,6 +86,23 @@ public class ListeningWhitelist {
return Objects.hashCode(priority, whitelist); return Objects.hashCode(priority, whitelist);
} }
/**
* Determine if any of the given IDs can be found in the whitelist.
* @param whitelist - whitelist to test.
* @param idList - list of packet IDs to find.
* @return TRUE if any of the packets in the list can be found in the whitelist, FALSE otherwise.
*/
public static boolean containsAny(ListeningWhitelist whitelist, int... idList) {
if (whitelist != null) {
for (int i = 0; i < idList.length; i++) {
if (whitelist.getWhitelist().contains(idList[i]))
return true;
}
}
return false;
}
@Override @Override
public boolean equals(final Object obj){ public boolean equals(final Object obj){
if(obj instanceof ListeningWhitelist){ if(obj instanceof ListeningWhitelist){

View File

@ -10,6 +10,9 @@ import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.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.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
@ -83,11 +86,13 @@ class NetworkFieldInjector extends PlayerInjector {
} }
} }
@Override @Override
public boolean canInject() { public void checkListener(PacketListener listener) {
// Probably // Unfortunately, we don't support chunk packets
return true; 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 @Override

View File

@ -11,6 +11,10 @@ import java.util.Set;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketListener;
/** /**
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method. * Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
* *
@ -43,11 +47,12 @@ class NetworkObjectInjector extends PlayerInjector {
} }
@Override @Override
public boolean canInject() { public void checkListener(PacketListener listener) {
// We only support 1.3.0 at the moment. Fixing it require us to // Unfortunately, we don't support chunk packets
// add jMock, which would add another dependency. if (ListeningWhitelist.containsAny(listener.getSendingWhitelist(),
return networkManager != null && Packets.Server.MAP_CHUNK, Packets.Server.MAP_CHUNK_BULK)) {
networkManagerField.getType().isInterface(); throw new IllegalStateException("The NETWORK_FIELD_INJECTOR hook doesn't support map chunk listeners.");
}
} }
@Override @Override

View File

@ -12,6 +12,7 @@ import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.comphenix.protocol.events.PacketListener;
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.instances.CollectionGenerator; import com.comphenix.protocol.reflect.instances.CollectionGenerator;
@ -66,6 +67,7 @@ public class NetworkServerInjector extends PlayerInjector {
@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
@ -142,10 +144,9 @@ public class NetworkServerInjector extends PlayerInjector {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override @Override
public boolean canInject() { public void checkListener(PacketListener listener) {
// Probably always // We support everything
return true;
} }
} }

View File

@ -74,7 +74,7 @@ public final class PacketFilterManager implements ProtocolManager {
NETWORK_MANAGER_OBJECT, NETWORK_MANAGER_OBJECT,
/** /**
* Override the server handler object. Versatile, but slow. * Override the server handler object. Versatile, but a tad slower.
*/ */
NETWORK_SERVER_OBJECT; NETWORK_SERVER_OBJECT;
} }
@ -106,6 +106,9 @@ public final class PacketFilterManager implements ProtocolManager {
// The default class loader // The default class loader
private ClassLoader classLoader; private ClassLoader classLoader;
// The last successful player hook
private PlayerInjector lastSuccessfulHook;
// Error logger // Error logger
private Logger logger; private Logger logger;
@ -142,6 +145,13 @@ public final class PacketFilterManager implements ProtocolManager {
*/ */
public void setPlayerHook(PlayerInjectHooks playerHook) { public void setPlayerHook(PlayerInjectHooks playerHook) {
this.playerHook = playerHook; this.playerHook = playerHook;
// Make sure the current listeners are compatible
if (lastSuccessfulHook != null) {
for (PacketListener listener : packetListeners) {
checkListener(listener);
}
}
} }
public Logger getLogger() { public Logger getLogger() {
@ -176,6 +186,9 @@ public final class PacketFilterManager implements ProtocolManager {
if (hasReceiving) { if (hasReceiving) {
recievedListeners.addListener(listener, receiving); recievedListeners.addListener(listener, receiving);
enablePacketFilters(ConnectionSide.CLIENT_SIDE, receiving.getWhitelist()); enablePacketFilters(ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
// We don't know if we've hooked any players yet
checkListener(listener);
} }
// Inform our injected hooks // Inform our injected hooks
@ -183,6 +196,20 @@ public final class PacketFilterManager implements ProtocolManager {
} }
} }
/**
* Determine if a listener is valid or not.
* @param listener - listener to check.
* @throws IllegalStateException If the given listener's whitelist cannot be fulfilled.
*/
public void checkListener(PacketListener listener) {
try {
if (lastSuccessfulHook != null)
lastSuccessfulHook.checkListener(listener);
} catch (Exception e) {
throw new IllegalStateException("Registering listener " + PacketAdapter.getPluginName(listener) + " failed", e);
}
}
@Override @Override
public void removePacketListener(PacketListener listener) { public void removePacketListener(PacketListener listener) {
if (listener == null) if (listener == null)
@ -367,15 +394,11 @@ public final class PacketFilterManager implements ProtocolManager {
/** /**
* Used to construct a player hook. * Used to construct a player hook.
* @param player - the player to hook. * @param player - the player to hook.
* @param hook - the hook type.
* @return A new player hoook * @return A new player hoook
* @throws IllegalAccessException Unable to do our reflection magic. * @throws IllegalAccessException Unable to do our reflection magic.
*/ */
protected PlayerInjector getPlayerHookInstance(Player player) throws IllegalAccessException { protected PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
return getHookInstance(player, playerHook);
}
// Helper
private PlayerInjector getHookInstance(Player player, PlayerInjectHooks hook) throws IllegalAccessException {
// Construct the correct player hook // Construct the correct player hook
switch (hook) { switch (hook) {
case NETWORK_HANDLER_FIELDS: case NETWORK_HANDLER_FIELDS:
@ -394,20 +417,42 @@ public final class PacketFilterManager implements ProtocolManager {
* @param player - player to hook. * @param player - player to hook.
*/ */
protected void injectPlayer(Player player) { protected void injectPlayer(Player player) {
PlayerInjector injector = null;
PlayerInjectHooks currentHook = playerHook;
boolean firstPlayer = lastSuccessfulHook == null;
// Don't inject if the class has closed // Don't inject if the class has closed
if (!hasClosed && player != null && !playerInjection.containsKey(player)) { if (!hasClosed && player != null && !playerInjection.containsKey(player)) {
try { while (true) {
PlayerInjector injector = getPlayerHookInstance(player); try {
injector = getHookInstance(player, currentHook);
injector.injectManager(); injector.injectManager();
playerInjection.put(player, injector); playerInjection.put(player, injector);
connectionLookup.put(injector.getInputStream(false), player); connectionLookup.put(injector.getInputStream(false), player);
break;
} catch (IllegalAccessException e) {
// Mark this injection attempt as a failure } catch (Exception e) {
playerInjection.put(player, null); // Mark this injection attempt as a failure
logger.log(Level.SEVERE, "Unable to access fields.", e); logger.log(Level.SEVERE, "Player hook " + currentHook.toString() + " failed.", e);
if (currentHook.ordinal() > 0) {
// Choose the previous player hook type
currentHook = PlayerInjectHooks.values()[currentHook.ordinal() - 1];
logger.log(Level.INFO, "Switching to " + currentHook.toString() + " instead.");
} else {
// UTTER FAILURE
playerInjection.put(player, null);
return;
}
}
} }
// Update values
if (injector != null)
lastSuccessfulHook = injector;
if (currentHook != playerHook || firstPlayer)
setPlayerHook(currentHook);
} }
} }

View File

@ -31,6 +31,7 @@ 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.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.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
@ -205,10 +206,12 @@ abstract class PlayerInjector {
public abstract void cleanupAll(); public abstract void cleanupAll();
/** /**
* Determine if we actually can inject. * Invoked before a new listener is registered.
* @return TRUE if this injector is compatible with the current CraftBukkit version, FALSE otherwise. * <p>
* The player injector should throw an exception if this listener cannot be properly supplied with packet events.
* @param listener - the listener that is about to be registered.
*/ */
public abstract boolean canInject(); public abstract void checkListener(PacketListener listener);
/** /**
* Allows a packet to be recieved by the listeners. * Allows a packet to be recieved by the listeners.