Added the ability to determine if any client or server packet is valid.

Also, added a warning when a plugin attempts to listen for a packet
ID that doesn't exist in the current Minecraft version.
This commit is contained in:
Kristian S. Stangeland 2012-10-10 02:41:34 +02:00
parent f8bd36bf3c
commit 6053b9e64b
4 changed files with 188 additions and 9 deletions

View File

@ -1,5 +1,9 @@
package com.comphenix.protocol;
import java.util.Set;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.IntEnum;
/**
@ -92,6 +96,25 @@ public final class Packets {
return INSTANCE;
}
/**
* Determine if the given packet is a valid server packet in the current version of Minecraft.
* @param packetID - the packet to test.
* @return TRUE if this packet is supported, FALSE otherwise.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
public static boolean isSupported(int packetID) throws FieldAccessException {
return PacketFilterManager.getServerPackets().contains(packetID);
}
/**
* Retrieve every client packet the current version of Minecraft is aware of.
* @return Every supported server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
public static Set<Integer> getSupported() throws FieldAccessException {
return PacketFilterManager.getServerPackets();
}
// We only allow a single instance of this class
private Server() {
super();
@ -146,6 +169,25 @@ public final class Packets {
return INSTANCE;
}
/**
* Determine if the given packet is a valid client packet in the current version of Minecraft.
* @param packetID - the packet to test.
* @return TRUE if this packet is supported, FALSE otherwise.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static boolean isSupported(int packetID) throws FieldAccessException {
return PacketFilterManager.getClientPackets().contains(packetID);
}
/**
* Retrieve every client packet the current version of Minecraft is aware of.
* @return Every supported client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static Set<Integer> getSupported() throws FieldAccessException {
return PacketFilterManager.getClientPackets();
}
// Like above
private Client() {
super();

View File

@ -19,13 +19,17 @@ package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraft.server.Packet;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
/**
* Static registries in Minecraft.
@ -35,9 +39,16 @@ import com.google.common.base.Objects;
@SuppressWarnings("rawtypes")
class MinecraftRegistry {
// Fuzzy reflection
private static FuzzyReflection packetRegistry;
// The packet class to packet ID translator
private static Map<Class, Integer> packetToID;
// Whether or not certain packets are sent by the client or the server
private static Set<Integer> serverPackets;
private static Set<Integer> clientPackets;
// New proxy values
private static Map<Integer, Class> overwrittenPackets = new HashMap<Integer, Class>();
@ -49,7 +60,7 @@ class MinecraftRegistry {
// Initialize it, if we haven't already
if (packetToID == null) {
try {
Field packetsField = FuzzyReflection.fromClass(Packet.class, true).getFieldByType("packetsField", Map.class);
Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class);
packetToID = (Map<Class, Integer>) FieldUtils.readStaticField(packetsField, true);
} catch (IllegalAccessException e) {
@ -60,14 +71,80 @@ class MinecraftRegistry {
return packetToID;
}
/**
* Retrieve the cached fuzzy reflection instance allowing access to the packet registry.
* @return Reflected packet registry.
*/
private static FuzzyReflection getPacketRegistry() {
if (packetRegistry == null)
packetRegistry = FuzzyReflection.fromClass(Packet.class, true);
return packetRegistry;
}
/**
* Retrieve the injected proxy classes handlig each packet ID.
* @return Injected classes.
*/
public static Map<Integer, Class> getOverwrittenPackets() {
return overwrittenPackets;
}
/**
* Retrieve the vanilla classes handling each packet ID.
* @return Vanilla classes.
*/
public static Map<Integer, Class> getPreviousPackets() {
return previousValues;
}
/**
* Retrieve every known and supported server packet.
* @return An immutable set of every known server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
public static Set<Integer> getServerPackets() throws FieldAccessException {
initializeSets();
return serverPackets;
}
/**
* Retrieve every known and supported client packet.
* @return An immutable set of every known client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static Set<Integer> getClientPackets() throws FieldAccessException {
initializeSets();
return clientPackets;
}
@SuppressWarnings("unchecked")
private static void initializeSets() throws FieldAccessException {
if (serverPackets == null || clientPackets == null) {
List<Field> sets = getPacketRegistry().getFieldListByType(Set.class);
try {
if (sets.size() > 1) {
serverPackets = (Set<Integer>) FieldUtils.readStaticField(sets.get(0), true);
clientPackets = (Set<Integer>) FieldUtils.readStaticField(sets.get(1), true);
// Impossible
if (serverPackets == null || clientPackets == null)
throw new FieldAccessException("Packet sets are in an illegal state.");
// NEVER allow callers to modify the underlying sets
serverPackets = ImmutableSet.copyOf(serverPackets);
clientPackets = ImmutableSet.copyOf(clientPackets);
} else {
throw new FieldAccessException("Cannot retrieve packet client/server sets.");
}
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access field.", e);
}
}
}
/**
* Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID.

View File

@ -107,6 +107,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// The async packet handler
private AsyncFilterManager asyncFilterManager;
// Valid server and client packets
private Set<Integer> serverPackets;
private Set<Integer> clientPackets;
/**
* Only create instances of this class if protocol lib is disabled.
*/
@ -123,6 +128,15 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, this, server);
this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
// Attempt to load the list of server and client packets
try {
this.serverPackets = MinecraftRegistry.getServerPackets();
this.clientPackets = MinecraftRegistry.getClientPackets();
} catch (FieldAccessException e) {
logger.log(Level.WARNING, "Cannot load server and client packet list.", e);
}
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Unable to initialize packet injector.", e);
}
@ -180,7 +194,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
if (hasSending) {
verifyWhitelist(listener, sending);
sendingListeners.addListener(listener, sending);
enablePacketFilters(ConnectionSide.SERVER_SIDE, sending.getWhitelist());
enablePacketFilters(listener, ConnectionSide.SERVER_SIDE, sending.getWhitelist());
// Make sure this is possible
playerInjection.checkListener(listener);
@ -188,7 +202,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
if (hasReceiving) {
verifyWhitelist(listener, receiving);
recievedListeners.addListener(listener, receiving);
enablePacketFilters(ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
enablePacketFilters(listener, ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
}
// Inform our injected hooks
@ -304,18 +318,39 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
* <p>
* Note that all packets are disabled by default.
*
* @param listener - the listener that requested to enable these filters.
* @param side - which side the event will arrive from.
* @param packets - the packet id(s).
*/
private void enablePacketFilters(ConnectionSide side, Iterable<Integer> packets) {
private void enablePacketFilters(PacketListener listener, ConnectionSide side, Iterable<Integer> packets) {
if (side == null)
throw new IllegalArgumentException("side cannot be NULL.");
// Note the difference between unsupported and valid.
// Every packet ID between and including 0 - 255 is valid, but only a subset is supported.
for (int packetID : packets) {
if (side.isForServer())
playerInjection.addPacketHandler(packetID);
if (side.isForClient() && packetInjector != null)
packetInjector.addPacketHandler(packetID);
// Only register server packets that are actually supported by Minecraft
if (side.isForServer()) {
if (serverPackets != null && serverPackets.contains(packetID))
playerInjection.addPacketHandler(packetID);
else
logger.warning(String.format(
"[%s] Unsupported server packet ID in current Minecraft version: %s",
PacketAdapter.getPluginName(listener), packetID
));
}
// As above, only for client packets
if (side.isForClient() && packetInjector != null) {
if (clientPackets != null && clientPackets.contains(packetID))
packetInjector.addPacketHandler(packetID);
else
logger.warning(String.format(
"[%s] Unsupported client packet ID in current Minecraft version: %s",
PacketAdapter.getPluginName(listener), packetID
));
}
}
}
@ -560,7 +595,25 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
e.printStackTrace();
}
}
/**
* Retrieve every known and supported server packet.
* @return An immutable set of every known server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
public static Set<Integer> getServerPackets() throws FieldAccessException {
return MinecraftRegistry.getServerPackets();
}
/**
* Retrieve every known and supported client packet.
* @return An immutable set of every known client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static Set<Integer> getClientPackets() throws FieldAccessException {
return MinecraftRegistry.getClientPackets();
}
/**
* Retrieves the current plugin class loader.
* @return Class loader.
@ -574,6 +627,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
return hasClosed;
}
/**
* Called when ProtocolLib is closing.
*/
public void close() {
// Guard
if (hasClosed)

View File

@ -117,6 +117,10 @@ class PacketInjector {
Map<Class, Integer> registry = MinecraftRegistry.getPacketToID();
Class old = MinecraftRegistry.getPacketClassFromID(packetID);
// If this packet is not known
if (old == null) {
throw new IllegalStateException("Packet ID " + packetID + " is not a valid packet ID in this version.");
}
// Check for previous injections
if (!old.getName().startsWith("net.minecraft.")) {
throw new IllegalStateException("Packet " + packetID + " has already been injected.");