/* * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. * Copyright (C) 2012 Kristian S. Stangeland * * 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 * 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; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * 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; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ 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. * * @author Kristian */ @SuppressWarnings("rawtypes") class MinecraftRegistry { // Fuzzy reflection private static FuzzyReflection packetRegistry; // The packet class to packet ID translator private static Map packetToID; // Whether or not certain packets are sent by the client or the server private static Set serverPackets; private static Set clientPackets; // New proxy values private static Map overwrittenPackets = new HashMap(); // Vanilla packets private static Map previousValues = new HashMap(); @SuppressWarnings({ "unchecked" }) public static Map getPacketToID() { // Initialize it, if we haven't already if (packetToID == null) { try { Field packetsField = getPacketRegistry().getFieldByType("packetsField", Map.class); packetToID = (Map) FieldUtils.readStaticField(packetsField, true); } catch (IllegalAccessException e) { throw new RuntimeException("Unable to retrieve the packetClassToIdMap", e); } } 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 getOverwrittenPackets() { return overwrittenPackets; } /** * Retrieve the vanilla classes handling each packet ID. * @return Vanilla classes. */ public static Map 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 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 getClientPackets() throws FieldAccessException { initializeSets(); return clientPackets; } @SuppressWarnings("unchecked") private static void initializeSets() throws FieldAccessException { if (serverPackets == null || clientPackets == null) { List sets = getPacketRegistry().getFieldListByType(Set.class); try { if (sets.size() > 1) { serverPackets = (Set) FieldUtils.readStaticField(sets.get(0), true); clientPackets = (Set) 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. * @return The associated class. */ public static Class getPacketClassFromID(int packetID) { return getPacketClassFromID(packetID, false); } /** * Retrieves the correct packet class from a given packet ID. * @param packetID - the packet ID. * @param vanilla - whether or not to look for vanilla classes, not injected classes. * @return The associated class. */ public static Class getPacketClassFromID(int packetID, boolean forceVanilla) { Map lookup = forceVanilla ? previousValues : overwrittenPackets; // Optimized lookup if (lookup.containsKey(packetID)) { return lookup.get(packetID); } // Will most likely not be used for (Map.Entry entry : getPacketToID().entrySet()) { if (Objects.equal(entry.getValue(), packetID)) { return entry.getKey(); } } throw new IllegalArgumentException("The packet ID " + packetID + " is not registered."); } }