diff --git a/modules/API/src/main/java/com/comphenix/protocol/PacketType.java b/modules/API/src/main/java/com/comphenix/protocol/PacketType.java index 7fbf7303..867b63c9 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/PacketType.java +++ b/modules/API/src/main/java/com/comphenix/protocol/PacketType.java @@ -31,7 +31,7 @@ import com.google.common.util.concurrent.Futures; * Note that vanilla Minecraft reuses packet IDs per protocol (ping, game, login) and IDs are subject to change, so they are not reliable. * @author Kristian */ -public class PacketType implements Serializable, Comparable { +public class PacketType implements Serializable, Cloneable, Comparable { // Increment whenever the type changes private static final long serialVersionUID = 1L; @@ -182,29 +182,53 @@ public class PacketType implements Serializable, Comparable { public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4B, 0x20, "UpdateAttributes"); public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4C, 0x1D, "EntityEffect"); - /** - * @deprecated Replaced by {@link WINDOW_DATA} - */ - @Deprecated - public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA; + // ---- Removed in 1.9 /** - * @deprecated Replaced by {@link REL_ENTITY_MOVE_LOOK} + * @deprecated Removed in 1.9 */ @Deprecated - public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK; + public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, -1, -1, "MapChunkBulk").deprecated(); /** - * @deprecated Replaced by {@link STATISTIC} + * @deprecated Removed in 1.9 */ @Deprecated - public static final PacketType STATISTICS = STATISTIC; + public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, -1, -1, "SetCompression").deprecated(); + + /** + * @deprecated Removed in 1.9 + */ + @Deprecated + public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, -1, -1, "UpdateEntityNBT").deprecated(); + + // ----- Renamed packets + + /** + * @deprecated Renamed to {@link WINDOW_DATA} + */ + @Deprecated + public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.deprecated(); + + /** + * @deprecated Renamed to {@link REL_ENTITY_MOVE_LOOK} + */ + @Deprecated + public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.deprecated(); + + /** + * @deprecated Renamed to {@link STATISTIC} + */ + @Deprecated + public static final PacketType STATISTICS = STATISTIC.deprecated(); + + // ----- Replaced in 1.9.4 /** * @deprecated Replaced by {@link TILE_ENTITY_DATA} */ @Deprecated - public static final PacketType UPDATE_SIGN = TILE_ENTITY_DATA; + public static final PacketType UPDATE_SIGN = TILE_ENTITY_DATA.deprecated(); // The instance must private final static Server INSTANCE = new Server(); @@ -297,7 +321,7 @@ public class PacketType implements Serializable, Comparable { * @deprecated Replaced by {@link SERVER_INFO} */ @Deprecated - public static final PacketType OUT_SERVER_INFO = SERVER_INFO; + public static final PacketType OUT_SERVER_INFO = SERVER_INFO.deprecated(); private final static Server INSTANCE = new Server(); @@ -562,6 +586,7 @@ public class PacketType implements Serializable, Comparable { private boolean forceAsync; private boolean dynamic; + private boolean deprecated; /** * Retrieve the current packet/legacy lookup. @@ -1043,6 +1068,16 @@ public class PacketType implements Serializable, Comparable { return forceAsync; } + private PacketType deprecated() { + PacketType ret = clone(); + ret.deprecated = true; + return ret; + } + + public boolean isDeprecated() { + return deprecated; + } + @Override public int hashCode() { return Objects.hashCode(protocol, sender, currentId, legacyId); @@ -1078,8 +1113,17 @@ public class PacketType implements Serializable, Comparable { Class clazz = getPacketClass(); if (clazz == null) - return "UNREGISTERED[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + "]"; + return name() + "[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]"; else return clazz.getSimpleName() + "[" + currentId + ", legacy: " + legacyId + "]"; } + + @Override + public PacketType clone() { + try { + return (PacketType) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new Error("This shouldn't happen", ex); + } + } } diff --git a/modules/API/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/modules/API/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index 439a2b41..67946386 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/modules/API/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -34,7 +34,7 @@ public class ProtocolLibrary { /** * The minimum version ProtocolLib has been tested with. */ - public static final String MINIMUM_MINECRAFT_VERSION = "1.9"; + public static final String MINIMUM_MINECRAFT_VERSION = "1.8"; /** * The maximum version ProtocolLib has been tested with. diff --git a/modules/API/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java b/modules/API/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java index fbe6b556..24aa57d3 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java +++ b/modules/API/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java @@ -18,6 +18,7 @@ package com.comphenix.protocol.injector.packet; import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -28,6 +29,7 @@ import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.injector.netty.NettyProtocolRegistry; import com.comphenix.protocol.injector.netty.ProtocolRegistry; import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Function; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -154,11 +156,10 @@ public class PacketRegistry { */ @Deprecated public static Set getServerPackets() throws FieldAccessException { - initialize(); - if (LEGACY_SERVER_PACKETS == null) { - LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets()); + LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes()); } + return LEGACY_SERVER_PACKETS; } @@ -168,9 +169,18 @@ public class PacketRegistry { */ public static Set getServerPacketTypes() { initialize(); - NETTY.synchronize(); - return NETTY.getServerPackets(); + + Set types = new HashSet<>(); + + // Filter out unsupported packets + for (PacketType type : NETTY.getServerPackets()) { + if (!type.isDeprecated()) { + types.add(type); + } + } + + return types; } /** @@ -182,11 +192,10 @@ public class PacketRegistry { */ @Deprecated public static Set getClientPackets() throws FieldAccessException { - initialize(); - if (LEGACY_CLIENT_PACKETS == null) { - LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets()); + LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes()); } + return LEGACY_CLIENT_PACKETS; } @@ -196,9 +205,18 @@ public class PacketRegistry { */ public static Set getClientPacketTypes() { initialize(); - NETTY.synchronize(); - return NETTY.getClientPackets(); + + Set types = new HashSet<>(); + + // Filter out unsupported packets + for (PacketType type : NETTY.getClientPackets()) { + if (!type.isDeprecated()) { + types.add(type); + } + } + + return types; } /** @@ -269,7 +287,24 @@ public class PacketRegistry { */ public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) { initialize(); - return NETTY.getPacketTypeLookup().get(type); + + // Try the lookup first + Class clazz = NETTY.getPacketTypeLookup().get(type); + if (clazz != null) { + return clazz; + } + + // Then try looking up the class names + for (String name : type.getClassNames()) { + try { + clazz = MinecraftReflection.getMinecraftClass(name); + break; + } catch (Exception ex) { + } + } + + // TODO Cache the result? + return clazz; } /** diff --git a/modules/API/src/main/java/com/comphenix/protocol/reflect/ObjectEnum.java b/modules/API/src/main/java/com/comphenix/protocol/reflect/ObjectEnum.java index c7828895..1345278b 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/reflect/ObjectEnum.java +++ b/modules/API/src/main/java/com/comphenix/protocol/reflect/ObjectEnum.java @@ -52,7 +52,7 @@ public class ObjectEnum implements Iterable { @SuppressWarnings("unchecked") protected void registerAll(Class fieldType) { try { - // Register every int field + // Register every non-deprecated field for (Field entry : this.getClass().getFields()) { if (Modifier.isStatic(entry.getModifiers()) && fieldType.isAssignableFrom(entry.getType())) { T value = (T) entry.get(null); @@ -63,7 +63,6 @@ public class ObjectEnum implements Iterable { registerMember(value, entry.getName()); } } - } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { diff --git a/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 3e7a059f..ffaa0608 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -152,6 +152,7 @@ public class MinecraftReflection { // Whether or not we are using netty private static Boolean cachedNetty; + private static Boolean cachedWatcherObject; private MinecraftReflection() { // No need to make this constructable. @@ -1313,16 +1314,23 @@ public class MinecraftReflection { } public static Class getDataWatcherObjectClass() { - // TODO Implement a fallback - return getMinecraftClass("DataWatcherObject"); + try { + return getMinecraftClass("DataWatcherObject"); + } catch (RuntimeException ex) { + return null; + } } public static boolean watcherObjectExists() { - try { - return getDataWatcherObjectClass() != null; - } catch (RuntimeException e) { - return false; + if (cachedWatcherObject == null) { + try { + return cachedWatcherObject = getDataWatcherObjectClass() != null; + } catch (Throwable ex) { + return cachedWatcherObject = false; + } } + + return cachedWatcherObject; } public static Class getDataWatcherSerializerClass() { diff --git a/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java b/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java index 2ac6a118..c2ac604b 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -17,6 +17,7 @@ package com.comphenix.protocol.wrappers; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -44,9 +45,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.collection.ConvertedMap; import com.google.common.base.Optional; +import com.google.common.collect.ImmutableBiMap; /** - * Represents a DataWatcher in 1.9 + * Represents a DataWatcher in 1.8 thru 1.10 * * @author dmulloy2 */ @@ -54,8 +56,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable HANDLE_TYPE = MinecraftReflection.getDataWatcherClass(); private static MethodAccessor GETTER = null; - private static MethodAccessor SETTER = null; - private static MethodAccessor REGISTER = null; + public static MethodAccessor SETTER = null; + public static MethodAccessor REGISTER = null; private static FieldAccessor ENTITY_DATA_FIELD = null; private static FieldAccessor ENTITY_FIELD = null; @@ -109,8 +111,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable objects) { this(); - for (WrappedWatchableObject object : objects) { - setObject(object.getWatcherObject(), object); + if (MinecraftReflection.watcherObjectExists()) { + for (WrappedWatchableObject object : objects) { + setObject(object.getWatcherObject(), object); + } + } else { + for (WrappedWatchableObject object : objects) { + setObject(object.getIndex(), object); + } } } @@ -217,6 +225,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable indexSet() { + return getMap().keySet(); + } + /** * Clears the contents of this DataWatcher. The watcher will be empty after * this operation is called. @@ -325,7 +349,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable methods = fuzzy.getMethodList(contract); + for (Method method : methods) { + if (method.getName().equals("set") || method.getName().equals("watch")) { + SETTER = Accessors.getMethodAccessor(method); + } else { + REGISTER = Accessors.getMethodAccessor(method); + } + } } + // Unwrap the object + value = WrappedWatchableObject.getUnwrapped(value); + if (hasIndex(object.getIndex())) { - SETTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value)); + SETTER.invoke(handle, object.getHandle(), value); } else { - Serializer serializer = object.getSerializer(); - Validate.notNull(serializer, "You must specify a serializer to register an object!"); - REGISTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value)); + object.checkSerializer(); + REGISTER.invoke(handle, object.getHandle(), value); } + + if (update) { + getWatchableObject(object.getIndex()).setDirtyState(update); + } + } + + /** + * Shortcut for {@link #setObject(WrappedDataWatcherObject, Object, boolean)} + */ + public void setObject(WrappedDataWatcherObject object, Object value) { + setObject(object, value, false); } // ---- Utility Methods @@ -438,8 +516,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder, Integer>() + .put(Byte.class, 0) + .put(Short.class, 1) + .put(Integer.class, 2) + .put(Float.class, 3) + .put(String.class, 4) + .put(MinecraftReflection.getItemStackClass(), 5) + .put(MinecraftReflection.getBlockPositionClass(), 6) + .put(Vector3F.getMinecraftClass(), 7) + .build(); + /** - * No longer supported in 1.9 due to the removal of a consistent type <-> ID map. + * Retrieves the type ID associated with a given class. No longer supported + * in 1.9 and up due to the removal of type IDs. * - * @param clazz - * @return Null + * @param clazz Class to find ID for + * @return The ID, or null if not found */ - @Deprecated public static Integer getTypeID(Class clazz) { - return null; + return CLASS_TO_ID.get(clazz); } /** - * No longer supported in 1.9 due to the removal of a consistent type <-> ID map. + * Retrieves the class associated with a given type ID. No longer + * supported in 1.9 and up due to the removal of type IDs. * - * @param typeID - * @return Null + * @param typeID ID to find Class for + * @return The Class, or null if not found */ - @Deprecated public static Class getTypeClass(int typeID) { - return null; + return CLASS_TO_ID.inverse().get(typeID); } @Override @@ -550,12 +645,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable HANDLE_TYPE = MinecraftReflection.getDataWatcherObjectClass(); private static ConstructorAccessor constructor = null; private static MethodAccessor getSerializer = null; - private final StructureModifier modifier; + private StructureModifier modifier; + private Object handle; + + protected WrappedDataWatcherObject() { + } /** * Creates a new watcher object from a NMS handle. @@ -563,9 +662,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable(HANDLE_TYPE).withTarget(handle); } @@ -579,6 +676,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable getHandleType() { + return HANDLE_TYPE; + } + @Override public String toString() { return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]"; @@ -641,6 +758,52 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable getHandleType() { + return int.class; + } + + @Override + public void checkSerializer() { + // Do nothing + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null) return false; + + if (obj instanceof DummyWatcherObject) { + DummyWatcherObject that = (DummyWatcherObject) obj; + return this.index == that.index; + } + + return false; + } + } + /** * Represents a DataWatcherSerializer in 1.9. If a Serializer is optional, * values must be wrapped in a {@link Optional}. diff --git a/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java b/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java index 95c8e050..3c3b8614 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/modules/API/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -30,18 +30,19 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObje import com.google.common.base.Optional; /** - * Represents a DataWatcher Item in 1.9. + * Represents a DataWatcher Item in 1.8 thru 1.10. * @author dmulloy2 */ - public class WrappedWatchableObject extends AbstractWrapper { private static final Class HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass(); + private static Integer VALUE_INDEX = null; + private static ConstructorAccessor constructor; private final StructureModifier modifier; /** - * Constructs a wrapped watchable object around an existing NMS data watcher item. + * Constructs a DataWatcher Item wrapper from an existing NMS data watcher item. * @param handle Data watcher item */ public WrappedWatchableObject(Object handle) { @@ -51,7 +52,18 @@ public class WrappedWatchableObject extends AbstractWrapper { } /** - * Constructs a wrapped watchable object with a given watcher object and initial value. + * Constructs a DataWatcher Item wrapper from a given index and initial value. + *

+ * Not recommended in 1.9 and up. + * @param index Index of the Item + * @param value Initial value + */ + public WrappedWatchableObject(int index, Object value) { + this(newHandle(WrappedDataWatcherObject.fromIndex(index), value)); + } + + /** + * Constructs a DataWatcher Item wrapper from a given watcher object and initial value. * @param watcherObject Watcher object * @param value Initial value */ @@ -64,7 +76,12 @@ public class WrappedWatchableObject extends AbstractWrapper { constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]); } - return constructor.invoke(watcherObject.getHandle(), value); + if (MinecraftReflection.watcherObjectExists()) { + return constructor.invoke(watcherObject.getHandle(), value); + } else { + // new WatchableObject(classId, index, value) + return constructor.invoke(WrappedDataWatcher.getTypeID(value.getClass()), watcherObject.getIndex(), value); + } } // ---- Getter methods @@ -82,7 +99,11 @@ public class WrappedWatchableObject extends AbstractWrapper { * @return The index */ public int getIndex() { - return getWatcherObject().getIndex(); + if (MinecraftReflection.watcherObjectExists()) { + return getWatcherObject().getIndex(); + } + + return modifier.withType(int.class).read(1); } /** @@ -98,7 +119,11 @@ public class WrappedWatchableObject extends AbstractWrapper { * @return Raw value */ public Object getRawValue() { - return modifier.readSafely(1); + if (VALUE_INDEX == null) { + VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2; + } + + return modifier.readSafely(VALUE_INDEX); } /** @@ -107,7 +132,11 @@ public class WrappedWatchableObject extends AbstractWrapper { * @param updateClient Whether or not to update the client */ public void setValue(Object value, boolean updateClient) { - modifier.write(1, getUnwrapped(value)); + if (VALUE_INDEX == null) { + VALUE_INDEX = MinecraftReflection.watcherObjectExists() ? 1 : 2; + } + + modifier.write(VALUE_INDEX, getUnwrapped(value)); if (updateClient) { setDirtyState(true); @@ -127,7 +156,7 @@ public class WrappedWatchableObject extends AbstractWrapper { * @return True if it must, false if not */ public boolean getDirtyState() { - return (boolean) modifier.read(2); + return modifier.withType(boolean.class).read(0); } /** @@ -135,7 +164,7 @@ public class WrappedWatchableObject extends AbstractWrapper { * @param dirty New state */ public void setDirtyState(boolean dirty) { - modifier.write(2, dirty); + modifier.withType(boolean.class).write(0, dirty); } @Override @@ -144,11 +173,10 @@ public class WrappedWatchableObject extends AbstractWrapper { if (obj == null) return false; if (obj instanceof WrappedWatchableObject) { - // watcher object, value, dirty state - WrappedWatchableObject other = (WrappedWatchableObject) obj; - return getWatcherObject().equals(other.getWatcherObject()) - && getRawValue().equals(other.getRawValue()) - && getDirtyState() == other.getDirtyState(); + WrappedWatchableObject that = (WrappedWatchableObject) obj; + return this.getIndex() == that.getIndex() && + this.getRawValue().equals(that.getRawValue()) && + this.getDirtyState() == that.getDirtyState(); } return false; @@ -156,7 +184,7 @@ public class WrappedWatchableObject extends AbstractWrapper { @Override public String toString() { - return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]"; + return "DataWatcherItem[index=" + getIndex() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]"; } // ---- Wrapping @@ -170,7 +198,12 @@ public class WrappedWatchableObject extends AbstractWrapper { */ @SuppressWarnings("rawtypes") static Object getWrapped(Object value) { - // Deal with optionals first + // Handle watcher items first + if (is(MinecraftReflection.getDataWatcherItemClass(), value)) { + return getWrapped(new WrappedWatchableObject(value).getRawValue()); + } + + // Then deal with optionals if (value instanceof Optional) { Optional optional = (Optional) value; if (optional.isPresent()) { diff --git a/modules/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/modules/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index ef238b7c..1afa5df8 100644 --- a/modules/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/modules/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -542,7 +542,7 @@ public class PacketContainerTest { public void testDeepClone() { // Try constructing all the packets for (PacketType type : PacketType.values()) { - if (BLACKLISTED.contains(type)) { + if (BLACKLISTED.contains(type) || type.isDeprecated()) { continue; } @@ -588,9 +588,7 @@ public class PacketContainerTest { } } catch (IllegalArgumentException e) { if (!registered) { - // Let the test pass - System.err.println("The packet ID " + type + " is not registered."); - assertEquals(e.getMessage(), "The packet ID " + type + " is not registered."); + e.printStackTrace(); } else { // Something is very wrong throw e;