> getMinecraftObjectMatcher() {
- if (fuzzyMatcher == null) {
- fuzzyMatcher = FuzzyMatchers.matchRegex(getMinecraftObjectRegex());
- }
- return fuzzyMatcher;
- }
-
- /**
- * Retrieve the name of the Minecraft server package.
- *
- * @return Full canonical name of the Minecraft server package.
- */
- public static String getMinecraftPackage() {
- // Speed things up
- if (MINECRAFT_FULL_PACKAGE != null) {
- return MINECRAFT_FULL_PACKAGE;
- }
-
- try {
- // get the bukkit version we're running on
- Server craftServer = Bukkit.getServer();
- CRAFTBUKKIT_PACKAGE = craftServer.getClass().getPackage().getName();
-
- // Parse the package version
- Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
- if (packageMatcher.matches()) {
- packageVersion = packageMatcher.group(1);
- } else {
- MinecraftVersion version = new MinecraftVersion(craftServer);
-
- // Just assume R1 - it's probably fine (warn anyway)
- packageVersion = "v" + version.getMajor() + "_" + version.getMinor() + "_R1";
- ProtocolLogger.log(Level.SEVERE, "Assuming package version: " + packageVersion);
- }
-
- if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
- // total rework of the NMS structure in 1.17 (at least there's no versioning)
- MINECRAFT_FULL_PACKAGE = MINECRAFT_PREFIX_PACKAGE = "net.minecraft";
- setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
- } else {
- // extract the server version from the return type of "getHandle" in CraftEntity
- Method getHandle = getCraftEntityClass().getMethod("getHandle");
- MINECRAFT_FULL_PACKAGE = getHandle.getReturnType().getPackage().getName();
-
- // Pretty important invariant
- if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
- // See if we got the Forge entity package
- if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
- // Use the standard NMS versioned package
- MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
- } else {
- // Assume they're the same instead
- MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
- }
-
- // The package is usually flat, so go with that assumption
- String matcher =
- (MINECRAFT_PREFIX_PACKAGE.length() > 0 ? Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
-
- // We'll still accept the default location, however
- setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_CLASS_NAME_REGEX + ")");
-
- } else {
- // Use the standard matcher
- setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
- }
- }
-
- return MINECRAFT_FULL_PACKAGE;
- } catch (NoSuchMethodException exception) {
- throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception);
- }
- }
-
- /**
- * Retrieve the package version of the underlying CraftBukkit server.
- *
- * @return The craftbukkit package version.
- */
- public static String getPackageVersion() {
- getMinecraftPackage();
- return packageVersion;
- }
-
- /**
- * Update the dynamic package matcher.
- *
- * @param regex - the Minecraft package regex.
- */
- private static void setDynamicPackageMatcher(String regex) {
- DYNAMIC_PACKAGE_MATCHER = regex;
- // Ensure that the matcher is regenerated
- fuzzyMatcher = null;
- }
-
- /**
- * Used during debugging and testing.
- *
- * @param minecraftPackage - the current Minecraft package.
- * @param craftBukkitPackage - the current CraftBukkit package.
- */
- static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
- MINECRAFT_FULL_PACKAGE = minecraftPackage;
- CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
-
- // Make sure it exists
- if (getMinecraftServerClass() == null) {
- throw new IllegalArgumentException("Cannot find MinecraftServer for package " + minecraftPackage);
- }
-
- // Standard matcher
- setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
- }
-
- /**
- * Retrieve the name of the root CraftBukkit package.
- *
- * @return Full canonical name of the root CraftBukkit package.
- */
- public static String getCraftBukkitPackage() {
- // Ensure it has been initialized
- if (CRAFTBUKKIT_PACKAGE == null) {
- getMinecraftPackage();
- }
-
- return CRAFTBUKKIT_PACKAGE;
- }
-
- /**
- * Dynamically retrieve the Bukkit entity from a given entity.
- *
- * @param nmsObject - the NMS entity.
- * @return A bukkit entity.
- * @throws RuntimeException If we were unable to retrieve the Bukkit entity.
- */
- public static Object getBukkitEntity(Object nmsObject) {
- if (nmsObject == null) {
- return null;
- }
-
- // We will have to do this dynamically, unfortunately
- try {
- Class> clazz = nmsObject.getClass();
- MethodAccessor accessor = BUKKIT_ENTITY_CACHE.get(clazz);
-
- if (accessor == null) {
- MethodAccessor created = Accessors.getMethodAccessor(clazz, "getBukkitEntity");
- accessor = BUKKIT_ENTITY_CACHE.putIfAbsent(clazz, created);
-
- // We won the race
- if (accessor == null) {
- accessor = created;
- }
- }
-
- return accessor.invoke(nmsObject);
- } catch (Exception e) {
- throw new IllegalArgumentException("Cannot get Bukkit entity from " + nmsObject, e);
- }
- }
-
- /**
- * Retrieve the Bukkit player from a given PlayerConnection.
- *
- * @param playerConnection The PlayerConnection.
- * @return A bukkit player.
- * @throws RuntimeException If we were unable to retrieve the Bukkit player.
- */
- public static Player getBukkitPlayerFromConnection(Object playerConnection) {
- try {
- return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection));
- } catch (Exception e) {
- throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e);
- }
- }
-
- /**
- * Determine if a given object can be found within the package net.minecraft.server.
- *
- * @param obj - the object to test.
- * @return TRUE if it can, FALSE otherwise.
- */
- public static boolean isMinecraftObject(Object obj) {
- if (obj == null) {
- return false;
- }
-
- // Doesn't matter if we don't check for the version here
- return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE);
- }
-
- /**
- * Determine if the given class is found within the package net.minecraft.server, or any equivalent package.
- *
- * @param clazz - the class to test.
- * @return TRUE if it can, FALSE otherwise.
- */
- public static boolean isMinecraftClass(Class> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz cannot be NULL.");
- }
-
- return getMinecraftObjectMatcher().isMatch(clazz, null);
- }
-
- /**
- * Determine if a given object is found in net.minecraft.server, and has the given name.
- *
- * @param obj - the object to test.
- * @param className - the class name to test.
- * @return TRUE if it can, FALSE otherwise.
- */
- public static boolean isMinecraftObject(Object obj, String className) {
- if (obj == null) {
- return false;
- }
-
- String javaName = obj.getClass().getName();
- return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
- }
-
- /**
- * Determine if a given Object is compatible with a given Class. That is, whether or not the Object is an instance of
- * that Class or one of its subclasses. If either is null, false is returned.
- *
- * @param clazz Class to test for, may be null
- * @param object the Object to test, may be null
- * @return True if it is, false if not
- * @see Class#isAssignableFrom(Class)
- */
- public static boolean is(Class> clazz, Object object) {
- if (clazz == null || object == null) {
- return false;
- }
-
- return clazz.isAssignableFrom(object.getClass());
- }
-
- /**
- * Equivalent to {@link #is(Class, Object)} but we don't call getClass again
- */
- public static boolean is(Class> clazz, Class> test) {
- if (clazz == null || test == null) {
- return false;
- }
-
- return clazz.isAssignableFrom(test);
- }
-
- /**
- * Determine if a given object is a BlockPosition.
- *
- * @param obj - the object to test.
- * @return TRUE if it can, FALSE otherwise.
- */
- public static boolean isBlockPosition(Object obj) {
- return is(getBlockPositionClass(), obj);
- }
-
- /**
- * Determine if the given object is an NMS ChunkCoordIntPar.
- *
- * @param obj - the object.
- * @return TRUE if it can, FALSE otherwise.
- */
- public static boolean isChunkCoordIntPair(Object obj) {
- return is(getChunkCoordIntPair(), obj);
- }
-
- /**
- * Determine if the given object is actually a Minecraft packet.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isPacketClass(Object obj) {
- return is(getPacketClass(), obj);
- }
-
- /**
- * Determine if the given object is assignable to a NetServerHandler (PlayerConnection)
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isServerHandler(Object obj) {
- return is(getPlayerConnectionClass(), obj);
- }
-
- /**
- * Determine if the given object is actually a Minecraft packet.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isMinecraftEntity(Object obj) {
- return is(getEntityClass(), obj);
- }
-
- /**
- * Determine if the given object is a NMS ItemStack.
- *
- * @param value - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isItemStack(Object value) {
- return is(getItemStackClass(), value);
- }
-
- /**
- * Determine if the given object is a CraftPlayer class.
- *
- * @param value - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isCraftPlayer(Object value) {
- return is(getCraftPlayerClass(), value);
- }
-
- /**
- * Determine if the given object is a Minecraft player entity.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isMinecraftPlayer(Object obj) {
- return is(getEntityPlayerClass(), obj);
- }
-
- /**
- * Determine if the given object is a data watcher object.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isDataWatcher(Object obj) {
- return is(getDataWatcherClass(), obj);
- }
-
- /**
- * Determine if the given object is an IntHashMap object.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isIntHashMap(Object obj) {
- return is(getIntHashMapClass(), obj);
- }
-
- /**
- * Determine if the given object is a CraftItemStack instancey.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isCraftItemStack(Object obj) {
- return is(getCraftItemStackClass(), obj);
- }
-
- /**
- * Retrieve the EntityPlayer (NMS) class.
- *
- * @return The entity class.
- */
- public static Class> getEntityPlayerClass() {
- try {
- return getMinecraftClass("server.level.EntityPlayer", "server.level.ServerPlayer", "EntityPlayer");
- } catch (RuntimeException e) {
- try {
- // Grab CraftPlayer's handle
- Method getHandle = FuzzyReflection
- .fromClass(getCraftBukkitClass("entity.CraftPlayer"))
- .getMethodByName("getHandle");
-
- // EntityPlayer is the return type
- return setMinecraftClass("EntityPlayer", getHandle.getReturnType());
- } catch (IllegalArgumentException e1) {
- throw new RuntimeException("Could not find EntityPlayer class.", e1);
- }
- }
- }
-
- /**
- * Retrieve the EntityHuman class.
- *
- * @return The entity human class.
- */
- public static Class> getEntityHumanClass() {
- // Assume its the direct superclass
- return getEntityPlayerClass().getSuperclass();
- }
-
- /**
- * Retrieve the GameProfile class.
- *
- * @return The game profile class.
- */
- public static Class> getGameProfileClass() {
- return getClass("com.mojang.authlib.GameProfile");
- }
-
- /**
- * Retrieve the entity (NMS) class.
- *
- * @return The entity class.
- */
- public static Class> getEntityClass() {
- try {
- return getMinecraftClass("world.entity.Entity", "Entity");
- } catch (RuntimeException e) {
- return fallbackMethodReturn("Entity", "entity.CraftEntity", "getHandle");
- }
- }
-
- /**
- * Retrieve the CraftChatMessage.
- *
- * @return The CraftChatMessage class.
- */
- public static Class> getCraftChatMessage() {
- return getCraftBukkitClass("util.CraftChatMessage");
- }
-
- /**
- * Retrieve the WorldServer (NMS) class.
- *
- * @return The WorldServer class.
- */
- public static Class> getWorldServerClass() {
- try {
- return getMinecraftClass("server.level.WorldServer", "server.level.ServerLevel", "WorldServer");
- } catch (RuntimeException e) {
- return fallbackMethodReturn("WorldServer", "CraftWorld", "getHandle");
- }
- }
-
- /**
- * Retrieve the World (NMS) class.
- *
- * @return The world class.
- */
- public static Class> getNmsWorldClass() {
- try {
- return getMinecraftClass("world.level.World", "world.level.Level", "World");
- } catch (RuntimeException e) {
- return setMinecraftClass("World", getWorldServerClass().getSuperclass());
- }
- }
-
- /**
- * Fallback on the return value of a named method in order to get a NMS class.
- *
- * @param nmsClass - the expected name of the Minecraft class.
- * @param craftClass - a CraftBukkit class to look at.
- * @param methodName - the method we will use.
- * @return The return value of this method, which will be saved to the package cache.
- */
- private static Class> fallbackMethodReturn(String nmsClass, String craftClass, String methodName) {
- Class> result = FuzzyReflection.fromClass(getCraftBukkitClass(craftClass))
- .getMethodByName(methodName)
- .getReturnType();
- // Save the result
- return setMinecraftClass(nmsClass, result);
- }
-
- /**
- * Retrieve the packet class.
- *
- * @return The packet class.
- */
- public static Class> getPacketClass() {
- return getMinecraftClass("network.protocol.Packet", "Packet");
- }
-
- public static Class> getByteBufClass() {
- return getClass("io.netty.buffer.ByteBuf");
- }
-
- /**
- * Retrieve the EnumProtocol class.
- *
- * @return The Enum protocol class.
- */
- public static Class> getEnumProtocolClass() {
- return getMinecraftClass("network.EnumProtocol", "network.ConnectionProtocol", "EnumProtocol");
- }
-
- /**
- * Retrieve the IChatBaseComponent class.
- *
- * @return The IChatBaseComponent.
- */
- public static Class> getIChatBaseComponentClass() {
- return getMinecraftClass("network.chat.IChatBaseComponent", "network.chat.IChatbaseComponent", "network.chat.Component", "IChatBaseComponent");
- }
-
- public static Class> getIChatBaseComponentArrayClass() {
- return getArrayClass(getIChatBaseComponentClass());
- }
-
- /**
- * Retrieve the NMS chat component text class.
- *
- * @return The chat component class.
- */
- public static Class> getChatComponentTextClass() {
- return getMinecraftClass("network.chat.ChatComponentText", "network.chat.TextComponent", "ChatComponentText");
- }
-
- /**
- * Attempt to find the ChatSerializer class.
- *
- * @return The serializer class.
- * @throws IllegalStateException If the class could not be found or deduced.
- */
- public static Class> getChatSerializerClass() {
- return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
- }
-
- /**
- * Retrieve the ServerPing class.
- *
- * @return The ServerPing class.
- */
- public static Class> getServerPingClass() {
- return getMinecraftClass("network.protocol.status.ServerPing", "network.protocol.status.ServerStatus", "ServerPing");
- }
-
- /**
- * Retrieve the ServerPingServerData class.
- *
- * @return The ServerPingServerData class.
- */
- public static Class> getServerPingServerDataClass() {
- return getMinecraftClass("network.protocol.status.ServerPing$ServerData", "network.protocol.status.ServerStatus$Version", "ServerPing$ServerData");
- }
-
- /**
- * Retrieve the ServerPingPlayerSample class.
- *
- * @return The ServerPingPlayerSample class.
- */
- public static Class> getServerPingPlayerSampleClass() {
- return getMinecraftClass(
- "network.protocol.status.ServerPing$ServerPingPlayerSample",
- "network.protocol.status.ServerStatus$Players",
- "ServerPing$ServerPingPlayerSample");
- }
-
- /**
- * Retrieve the MinecraftServer class.
- *
- * @return MinecraftServer class.
- */
- public static Class> getMinecraftServerClass() {
- try {
- return getMinecraftClass("server.MinecraftServer", "MinecraftServer");
- } catch (RuntimeException e) {
- // Reset cache and try again
- setMinecraftClass("MinecraftServer", null);
-
- useFallbackServer();
- return getMinecraftClass("MinecraftServer");
- }
- }
-
- /**
- * Retrieve the NMS statistics class.
- *
- * @return The statistics class.
- */
- public static Class> getStatisticClass() {
- return getMinecraftClass("stats.Statistic", "stats.Stat", "Statistic");
- }
-
- /**
- * Retrieve the NMS statistic list class.
- *
- * @return The statistic list class.
- */
- public static Class> getStatisticListClass() {
- return getMinecraftClass("stats.StatisticList", "stats.Stats", "StatisticList");
- }
-
- /**
- * Retrieve the player list class (or ServerConfigurationManager),
- *
- * @return The player list class.
- */
- public static Class> getPlayerListClass() {
- try {
- return getMinecraftClass("server.players.PlayerList", "PlayerList");
- } catch (RuntimeException e) {
- // Reset cache and try again
- setMinecraftClass("PlayerList", null);
-
- useFallbackServer();
- return getMinecraftClass("PlayerList");
- }
- }
-
- /**
- * Retrieve the PlayerConnection class.
- *
- * @return The PlayerConnection class.
- */
- public static Class> getPlayerConnectionClass() {
- return getMinecraftClass("server.network.PlayerConnection", "server.network.ServerGamePacketListenerImpl", "PlayerConnection");
- }
-
- /**
- * Retrieve the NetworkManager class.
- *
- * @return The NetworkManager class.
- */
- public static Class> getNetworkManagerClass() {
- return getMinecraftClass("network.NetworkManager", "network.Connection", "NetworkManager");
- }
-
- /**
- * Retrieve the NMS ItemStack class.
- *
- * @return The ItemStack class.
- */
- public static Class> getItemStackClass() {
- try {
- return getMinecraftClass("world.item.ItemStack", "ItemStack");
- } catch (RuntimeException e) {
- // Use the handle reference
- return setMinecraftClass("ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
- .getFieldByName("handle")
- .getType());
- }
- }
-
- /**
- * Retrieve the Block (NMS) class.
- *
- * @return Block (NMS) class.
- */
- public static Class> getBlockClass() {
- return getMinecraftClass("world.level.block.Block", "Block");
- }
-
- public static Class> getItemClass() {
- return getNullableNMS("world.item.Item", "Item");
- }
-
- public static Class> getFluidTypeClass() {
- return getNullableNMS("world.level.material.FluidType", "world.level.material.Fluid", "FluidType");
- }
-
- public static Class> getParticleTypeClass() {
- return getNullableNMS("core.particles.ParticleType", "core.particles.SimpleParticleType", "ParticleType");
- }
-
- /**
- * Retrieve the WorldType class.
- *
- * @return The WorldType class.
- */
- public static Class> getWorldTypeClass() {
- return getMinecraftClass("WorldType");
- }
-
- /**
- * Retrieve the DataWatcher class.
- *
- * @return The DataWatcher class.
- */
- public static Class> getDataWatcherClass() {
- return getMinecraftClass("network.syncher.DataWatcher", "network.syncher.SynchedEntityData", "DataWatcher");
- }
-
- /**
- * Retrieves the BlockPosition class.
- *
- * @return The BlockPosition class.
- */
- public static Class> getBlockPositionClass() {
- return getMinecraftClass("core.BlockPosition", "core.BlockPos", "BlockPosition");
- }
-
- /**
- * Retrieves the Vec3D class.
- *
- * @return The Vec3D class.
- */
- public static Class> getVec3DClass() {
- return getMinecraftClass("world.phys.Vec3D", "world.phys.Vec3", "Vec3D");
- }
-
- /**
- * Retrieve the ChunkCoordIntPair class.
- *
- * @return The ChunkCoordIntPair class.
- */
- public static Class> getChunkCoordIntPair() {
- return getMinecraftClass("world.level.ChunkCoordIntPair", "world.level.ChunkPos", "ChunkCoordIntPair");
- }
-
- /**
- * Retrieve the DataWatcher Item class.
- *
- * @return The class
- */
- public static Class> getDataWatcherItemClass() {
- return getMinecraftClass("network.syncher.DataWatcher$Item", "network.syncher.SynchedEntityData$DataItem", "DataWatcher$Item", "DataWatcher$WatchableObject");
- }
-
- public static Class> getDataWatcherObjectClass() {
- return getNullableNMS("network.syncher.DataWatcherObject", "network.syncher.EntityDataAccessor", "DataWatcherObject");
- }
-
- public static boolean watcherObjectExists() {
- if (cachedWatcherObject == null) {
- cachedWatcherObject = getDataWatcherObjectClass() != null;
- }
-
- return cachedWatcherObject;
- }
-
- public static Class> getDataWatcherSerializerClass() {
- return getNullableNMS("network.syncher.DataWatcherSerializer", "network.syncher.EntityDataSerializer", "DataWatcherSerializer");
- }
-
- public static Class> getDataWatcherRegistryClass() {
- return getMinecraftClass("network.syncher.DataWatcherRegistry", "network.syncher.EntityDataSerializers", "DataWatcherRegistry");
- }
-
- public static Class> getMinecraftKeyClass() {
- return getMinecraftClass("resources.MinecraftKey", "resources.ResourceLocation", "MinecraftKey");
- }
-
- public static Class> getMobEffectListClass() {
- return getMinecraftClass("world.effect.MobEffectList", "MobEffectList", "world.effect.MobEffect");
- }
-
- public static Class> getSoundEffectClass() {
- return getNullableNMS("sounds.SoundEffect", "sounds.SoundEvent", "SoundEffect");
- }
-
- /**
- * Retrieve the ServerConnection abstract class.
- *
- * @return The ServerConnection class.
- */
- public static Class> getServerConnectionClass() {
- return getMinecraftClass("server.network.ServerConnection", "server.network.ServerConnectionListener", "ServerConnection");
- }
-
- /**
- * Retrieve the NBT base class.
- *
- * @return The NBT base class.
- */
- public static Class> getNBTBaseClass() {
- return getMinecraftClass("nbt.NBTBase", "nbt.Tag", "NBTBase");
- }
-
- /**
- * Retrieve the NBT read limiter class.
- *
- * @return The NBT read limiter.
- */
- public static Class> getNBTReadLimiterClass() {
- return getMinecraftClass("nbt.NBTReadLimiter", "nbt.NbtAccounter", "NBTReadLimiter");
- }
-
- /**
- * Retrieve the NBT Compound class.
- *
- * @return The NBT Compond class.
- */
- public static Class> getNBTCompoundClass() {
- return getMinecraftClass("nbt.NBTTagCompound", "nbt.CompoundTag", "NBTTagCompound");
- }
-
- /**
- * Retrieve the EntityTracker (NMS) class.
- *
- * @return EntityTracker class.
- */
- public static Class> getEntityTrackerClass() {
- return getMinecraftClass("server.level.PlayerChunkMap$EntityTracker", "server.level.ChunkMap$TrackedEntity", "EntityTracker");
- }
-
- /**
- * Retrieve the attribute snapshot class.
- *
- * This stores the final value of an attribute, along with all the associated computational steps.
- *
- * @return The attribute snapshot class.
- */
- public static Class> getAttributeSnapshotClass() {
- return getMinecraftClass(
- "network.protocol.game.PacketPlayOutUpdateAttributes$AttributeSnapshot",
- "network.protocol.game.ClientboundUpdateAttributesPacket$AttributeSnapshot",
- "AttributeSnapshot",
- "PacketPlayOutUpdateAttributes$AttributeSnapshot");
- }
-
- /**
- * Retrieve the IntHashMap class.
- *
- * @return IntHashMap class.
- */
- public static Class> getIntHashMapClass() {
- return getNullableNMS("IntHashMap");
- }
-
- /**
- * Retrieve the attribute modifier class.
- *
- * @return Attribute modifier class.
- */
- public static Class> getAttributeModifierClass() {
- return getMinecraftClass("world.entity.ai.attributes.AttributeModifier", "AttributeModifier");
- }
-
- /**
- * Retrieve the net.minecraft.server.MobEffect class.
- *
- * @return The mob effect class.
- */
- public static Class> getMobEffectClass() {
- return getMinecraftClass("world.effect.MobEffect", "world.effect.MobEffectInstance", "MobEffect");
- }
-
- /**
- * Retrieve the packet data serializer class that overrides ByteBuf.
- *
- * @return The data serializer class.
- */
- public static Class> getPacketDataSerializerClass() {
- return getMinecraftClass("network.PacketDataSerializer", "network.FriendlyByteBuf", "PacketDataSerializer");
- }
-
- /**
- * Retrieve the NBTCompressedStreamTools class.
- *
- * @return The NBTCompressedStreamTools class.
- */
- public static Class> getNbtCompressedStreamToolsClass() {
- return getMinecraftClass("nbt.NBTCompressedStreamTools", "nbt.NbtIo", "NBTCompressedStreamTools");
- }
-
- /**
- * Retrieve the NMS tile entity class.
- *
- * @return The tile entity class.
- */
- public static Class> getTileEntityClass() {
- return getMinecraftClass("world.level.block.entity.TileEntity", "world.level.block.entity.BlockEntity", "TileEntity");
- }
-
- /**
- * Retrieve the Gson class used by Minecraft.
- *
- * @return The Gson class.
- */
- public static Class> getMinecraftGsonClass() {
- return getMinecraftLibraryClass("com.google.gson.Gson");
- }
-
- /**
- * Retrieve the ItemStack[] class.
- *
- * @return The ItemStack[] class.
- */
- public static Class> getItemStackArrayClass() {
- if (itemStackArrayClass == null) {
- itemStackArrayClass = getArrayClass(getItemStackClass());
- }
- return itemStackArrayClass;
- }
-
- /**
- * Retrieve the array class of a given component type.
- *
- * @param componentType - type of each element in the array.
- * @return The class of the array.
- */
- public static Class> getArrayClass(Class> componentType) {
- // A bit of a hack, but it works
- return Array.newInstance(componentType, 0).getClass();
- }
-
- /**
- * Retrieve the CraftItemStack class.
- *
- * @return The CraftItemStack class.
- */
- public static Class> getCraftItemStackClass() {
- return getCraftBukkitClass("inventory.CraftItemStack");
- }
-
- /**
- * Retrieve the CraftPlayer class.
- *
- * @return CraftPlayer class.
- */
- public static Class> getCraftPlayerClass() {
- return getCraftBukkitClass("entity.CraftPlayer");
- }
-
- /**
- * Retrieve the CraftWorld class.
- *
- * @return The CraftWorld class.
- */
- public static Class> getCraftWorldClass() {
- return getCraftBukkitClass("CraftWorld");
- }
-
- /**
- * Retrieve the CraftEntity class.
- *
- * @return CraftEntity class.
- */
- public static Class> getCraftEntityClass() {
- return getCraftBukkitClass("entity.CraftEntity");
- }
-
- /**
- * Retrieve the CraftChatMessage introduced in 1.7.2
- *
- * @return The CraftChatMessage class.
- */
- public static Class> getCraftMessageClass() {
- return getCraftBukkitClass("util.CraftChatMessage");
- }
-
- /**
- * Retrieve the PlayerInfoData class in 1.8.
- *
- * @return The PlayerInfoData class
- */
- public static Class> getPlayerInfoDataClass() {
- return getMinecraftClass(
- "network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
- "network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
- "PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
- }
-
- /**
- * Retrieves the entity use action class in 1.17.
- *
- * @return The EntityUseAction class
- */
- public static Class> getEnumEntityUseActionClass() {
- Class> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
- FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
- try {
- return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
- } catch (IllegalArgumentException ignored) {
- return fuzzyReflection.getFieldByType("^.*(Action)").getType();
- }
- }
-
- /**
- * Get a method accessor to get the actual use action out of the wrapping EnumEntityUseAction in 1.17.
- *
- * @return a method accessor to get the actual use action
- */
- public static MethodAccessor getEntityUseActionEnumMethodAccessor() {
- FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
- return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
- .returnTypeExact(EnumWrappers.getEntityUseActionClass())
- .build()));
- }
-
- /**
- * Get a field accessor for the hand in the wrapping EnumEntityUseAction in 1.17.
- *
- * @param enumEntityUseAction the object instance of the action, the field is not present in attack.
- * @return a field accessor for the hand in the wrapping EnumEntityUseAction
- */
- public static FieldAccessor getHandEntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
- FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
- return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
- .typeExact(EnumWrappers.getHandClass())
- .build()));
- }
-
- /**
- * Get a field accessor for the vec3d in the wrapping EnumEntityUseAction in 1.17.
- *
- * @param enumEntityUseAction the object instance of the action, the field is not present in attack.
- * @return a field accessor for the hand in the wrapping EnumEntityUseAction
- */
- public static FieldAccessor getVec3EntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
- FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
- return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
- .typeExact(MinecraftReflection.getVec3DClass())
- .build()));
- }
-
- /**
- * Determine if the given object is a PlayerInfoData.
- *
- * @param obj - the given object.
- * @return TRUE if it is, FALSE otherwise.
- */
- public static boolean isPlayerInfoData(Object obj) {
- return is(getPlayerInfoDataClass(), obj);
- }
-
- /**
- * Retrieve the IBlockData class in 1.8.
- *
- * @return The IBlockData class
- */
- public static Class> getIBlockDataClass() {
- return getMinecraftClass("world.level.block.state.IBlockData", "world.level.block.state.BlockState", "IBlockData");
- }
-
- /**
- * Retrieve the MultiBlockChangeInfo class in 1.8.
- *
- * @return The MultiBlockChangeInfo class
- */
- public static Class> getMultiBlockChangeInfoClass() {
- return getMinecraftClass("MultiBlockChangeInfo", "PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
- }
-
- /**
- * Retrieve the MultiBlockChangeInfo array class in 1.8.
- *
- * @return The MultiBlockChangeInfo array class
- */
- public static Class> getMultiBlockChangeInfoArrayClass() {
- return getArrayClass(getMultiBlockChangeInfoClass());
- }
-
- /**
- * Retrieve the PacketPlayOutGameStateChange.a class, aka GameState in 1.16
- *
- * @return The GameState class
- */
- public static Class> getGameStateClass() {
- // it's called "a" so there's not a lot we can do to identify it
- Class> packetClass = PacketType.Play.Server.GAME_STATE_CHANGE.getPacketClass();
- return packetClass.getClasses()[0];
- }
-
- public static boolean signUpdateExists() {
- return getNullableNMS("PacketPlayOutUpdateSign") != null;
- }
-
- public static Class> getNonNullListClass() {
- return getMinecraftClass("core.NonNullList", "NonNullList");
- }
-
- public static MethodAccessor getNonNullListCreateAccessor() {
- try {
- Class> nonNullListType = MinecraftReflection.getNonNullListClass();
- Method method = FuzzyReflection.fromClass(nonNullListType).getMethod(FuzzyMethodContract.newBuilder()
- .returnTypeExact(nonNullListType)
- .requireModifier(Modifier.STATIC)
- .build());
- return Accessors.getMethodAccessor(method);
- } catch (Exception ex) {
- return null;
- }
- }
-
- public static Class> getCraftSoundClass() {
- return getCraftBukkitClass("CraftSound");
- }
-
- public static Class> getSectionPositionClass() {
- return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
- }
-
- /**
- * Retrieves the Bukkit equivalent of a NMS ItemStack. This method should preserve NBT data and will never return null
- * when supplied with a valid ItemStack. Empty ItemStacks are treated as AIR.
- *
- * @param generic NMS ItemStack
- * @return The Bukkit equivalent
- */
- public static ItemStack getBukkitItemStack(Object generic) {
- if (generic == null) {
- // Convert null to AIR - 1.11 behavior
- return new ItemStack(Material.AIR);
- }
-
- if (generic instanceof ItemStack) {
- ItemStack bukkit = (ItemStack) generic;
-
- // They're probably looking for the CraftItemStack
- // If it's one already our work is done
- if (is(getCraftItemStackClass(), generic)) {
- return bukkit;
- }
-
- // If not, convert it to one
- Object nmsStack = getMinecraftItemStack(bukkit);
- return getBukkitItemStack(nmsStack);
- }
-
- if (!is(getItemStackClass(), generic)) {
- // We can't do anything with non-ItemStacks
- throw new IllegalArgumentException(generic + " is not an ItemStack!");
- }
-
- try {
- // Check null enforcement - 1.11 behavior
- if (nullEnforced == null) {
- isEmpty = Accessors.getMethodAccessor(getItemStackClass().getMethod("isEmpty"));
- nullEnforced = true;
- }
-
- if (nullEnforced) {
- if ((boolean) isEmpty.invoke(generic)) {
- return new ItemStack(Material.AIR);
- }
- }
- } catch (ReflectiveOperationException ex) {
- nullEnforced = false;
- }
-
- if (asCraftMirror == null) {
- try {
- Method asMirrorMethod = getCraftItemStackClass().getMethod("asCraftMirror", getItemStackClass());
- asCraftMirror = Accessors.getMethodAccessor(asMirrorMethod);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException("Failed to obtain CraftItemStack.asCraftMirror", ex);
- }
- }
-
- try {
- // Convert to a craft mirror to preserve NBT data
- return (ItemStack) asCraftMirror.invoke(nullEnforced, generic);
- } catch (IllegalStateException ex) {
- throw new RuntimeException("Failed to obtain craft mirror of " + generic, ex);
- }
- }
-
- /**
- * Retrieves the NMS equivalent of a Bukkit ItemStack. This method will never return null and should preserve NBT
- * data. Null inputs are treated as empty (AIR) ItemStacks.
- *
- * @param specific Bukkit ItemStack
- * @return The NMS equivalent
- */
- public static Object getMinecraftItemStack(ItemStack specific) {
- if (asNMSCopy == null) {
- try {
- Method asNmsCopyMethod = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
- asNMSCopy = Accessors.getMethodAccessor(asNmsCopyMethod);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException("Failed to obtain CraftItemStack.asNMSCopy", ex);
- }
- }
-
- if (is(getCraftItemStackClass(), specific)) {
- // If it's already a CraftItemStack, use its handle
- Object unwrapped = BukkitUnwrapper.getInstance().unwrapItem(specific);
- if (unwrapped != null) {
- return unwrapped;
- } else {
- if (itemStackAir == null) {
- // Easiest way to get the Material.AIR ItemStack?
- itemStackAir = getMinecraftItemStack(new ItemStack(Material.AIR));
- }
- return itemStackAir;
- }
- }
-
- try {
- // If not, grab a NMS copy
- return asNMSCopy.invoke(null, specific);
- } catch (IllegalStateException ex) {
- throw new RuntimeException("Failed to make NMS copy of " + specific, ex);
- }
- }
-
- /**
- * Retrieve the given class by name.
- *
- * @param className - name of the class.
- * @return The class.
- */
- private static Class> getClass(String className) {
- try {
- return getClassSource().loadClass(className);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException("Cannot find class " + className, e);
- }
- }
-
- /**
- * Retrieve the class object of a specific CraftBukkit class.
- *
- * @param className - the specific CraftBukkit class.
- * @return Class object.
- * @throws RuntimeException If we are unable to find the given class.
- */
- public static Class> getCraftBukkitClass(String className) {
- if (craftbukkitPackage == null) {
- craftbukkitPackage = new CachedPackage(getCraftBukkitPackage(), getClassSource());
- }
-
- return craftbukkitPackage.getPackageClass(className)
- .orElseThrow(() -> new RuntimeException("Failed to find CraftBukkit class: " + className));
- }
-
- /**
- * Retrieve the class object of a specific Minecraft class.
- *
- * @param className - the specific Minecraft class.
- * @return Class object.
- * @throws RuntimeException If we are unable to find the given class.
- */
- public static Class> getMinecraftClass(String className) {
- if (minecraftPackage == null) {
- minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
- }
-
- return minecraftPackage.getPackageClass(className)
- .orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
- }
-
- public static Class> getNullableNMS(String className, String... aliases) {
- try {
- return getMinecraftClass(className, aliases);
- } catch (RuntimeException ex) {
- return null;
- }
- }
-
- /**
- * Set the class object for the specific Minecraft class.
- *
- * @param className - name of the Minecraft class.
- * @param clazz - the new class object.
- * @return The provided clazz object.
- */
- private static Class> setMinecraftClass(String className, Class> clazz) {
- if (minecraftPackage == null) {
- minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
- }
-
- minecraftPackage.setPackageClass(className, clazz);
- return clazz;
- }
-
- /**
- * Retrieve the current class source.
- *
- * @return The class source.
- */
- private static ClassSource getClassSource() {
- return CLASS_SOURCE;
- }
-
- /**
- * Retrieve the first class that matches a specified Minecraft name.
- *
- * @param className - the specific Minecraft class.
- * @param aliases - alternative names for this Minecraft class.
- * @return Class object.
- * @throws RuntimeException If we are unable to find any of the given classes.
- */
- public static Class> getMinecraftClass(String className, String... aliases) {
- if (minecraftPackage == null) {
- minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
- }
-
- return minecraftPackage.getPackageClass(className).orElseGet(() -> {
- Class> resolved = null;
- for (String alias : aliases) {
- // try to resolve the class and stop searching if we found it
- resolved = minecraftPackage.getPackageClass(alias).orElse(null);
- if (resolved != null) {
- break;
- }
- }
-
- // if we resolved the class cache it and return the result
- if (resolved != null) {
- minecraftPackage.setPackageClass(className, resolved);
- return resolved;
- }
-
- // unable to find the class
- throw new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases)));
- });
- }
-
- /**
- * Retrieve the class object of a specific Minecraft library class.
- *
- * @param className - the specific library Minecraft class.
- * @return Class object.
- * @throws RuntimeException If we are unable to find the given class.
- */
- public static Class> getMinecraftLibraryClass(String className) {
- if (libraryPackage == null) {
- libraryPackage = new CachedPackage("", getClassSource());
- }
-
- return libraryPackage.getPackageClass(className)
- .orElseThrow(() -> new RuntimeException("Failed to find class: " + className));
- }
-
- /**
- * Set the class object for the specific library class.
- *
- * @param className - name of the Minecraft library class.
- * @param clazz - the new class object.
- * @return The provided clazz object.
- */
- private static Class> setMinecraftLibraryClass(String className, Class> clazz) {
- if (libraryPackage == null) {
- libraryPackage = new CachedPackage("", getClassSource());
- }
-
- libraryPackage.setPackageClass(className, clazz);
- return clazz;
- }
-
- /**
- * Dynamically retrieve the NetworkManager name.
- *
- * @return Name of the NetworkManager class.
- */
- public static String getNetworkManagerName() {
- return getNetworkManagerClass().getSimpleName();
- }
-
- /**
- * Retrieve an instance of the packet data serializer wrapper.
- *
- * @param buffer - the buffer.
- * @return The instance.
- */
- public static Object getPacketDataSerializer(Object buffer) {
- try {
- // TODO: move this to MinecraftMethods, or at least, cache the constructor accessor
- Class> packetSerializer = getPacketDataSerializerClass();
- return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer);
- } catch (Exception e) {
- throw new RuntimeException("Cannot construct packet serializer.", e);
- }
- }
-
- public static Object createPacketDataSerializer(int initialSize) {
- // validate the initial size
- if (initialSize <= 0) {
- initialSize = 256;
- }
-
- Object buffer = Unpooled.buffer(initialSize);
- return getPacketDataSerializer(buffer);
- }
-
- public static Class> getNbtTagTypes() {
- return getMinecraftClass("nbt.NBTTagTypes", "nbt.TagTypes", "NBTTagTypes");
- }
-
- public static Class> getChatDeserializer() {
- return getMinecraftClass("util.ChatDeserializer", "util.GsonHelper", "ChatDeserializer");
- }
-
- public static Class> getChatMutableComponentClass() {
- return getMinecraftClass("network.chat.IChatMutableComponent", "network.chat.MutableComponent");
- }
-
- public static Class> getDimensionManager() {
- return getMinecraftClass("world.level.dimension.DimensionManager", "world.level.dimension.DimensionType", "DimensionManager");
- }
-
- public static Class> getMerchantRecipeList() {
- return getMinecraftClass("world.item.trading.MerchantRecipeList", "world.item.trading.MerchantOffers", "MerchantRecipeList");
- }
-
- public static Class> getResourceKey() {
- return getMinecraftClass("resources.ResourceKey", "ResourceKey");
- }
-
- public static Class> getEntityTypes() {
- return getMinecraftClass("world.entity.EntityTypes", "world.entity.EntityType", "EntityTypes");
- }
-
- public static Class> getParticleParam() {
- return getMinecraftClass("core.particles.ParticleParam", "core.particles.ParticleOptions", "ParticleParam");
- }
-
- public static Class> getSectionPosition() {
- return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
- }
-
- public static Class> getChunkProviderServer() {
- return getMinecraftClass("server.level.ChunkProviderServer", "server.level.ServerChunkCache", "ChunkProviderServer");
- }
-
- public static Class> getPlayerChunkMap() {
- return getMinecraftClass("server.level.PlayerChunkMap", "server.level.ChunkMap", "PlayerChunkMap");
- }
-
- public static Class> getIRegistry() {
- return getNullableNMS("core.IRegistry", "core.Registry", "IRegistry");
- }
-
- public static Class> getAttributeBase() {
- return getMinecraftClass("world.entity.ai.attributes.AttributeBase", "world.entity.ai.attributes.Attribute", "AttributeBase");
- }
-
- public static Class> getProfilePublicKeyClass() {
- return getMinecraftClass("world.entity.player.ProfilePublicKey");
- }
-
- public static Class> getSaltedSignatureClass() {
- try {
- return getMinecraftClass("SaltedSignature");
- } catch (RuntimeException runtimeException) {
- Class> minecraftEncryption = getMinecraftClass("util.MinecraftEncryption", "util.Crypt", "MinecraftEncryption");
- FuzzyMethodContract constructorContract = FuzzyMethodContract.newBuilder()
- .parameterCount(2)
- .parameterExactType(Long.TYPE, 0)
- .parameterExactType(byte[].class, 1)
- .build();
-
- for (Class> subclass : minecraftEncryption.getClasses()) {
- FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(subclass, true);
- List> constructors = fuzzyReflection.getConstructorList(constructorContract);
-
- if (!constructors.isEmpty()) {
- return setMinecraftClass("SaltedSignature", subclass);
- }
- }
-
- Class> messageSigClass = getMinecraftClass("network.chat.MessageSignature", "MessageSignature");
- FuzzyClassContract signatureContract = FuzzyClassContract.newBuilder()
- .constructor(FuzzyMethodContract.newBuilder()
- .parameterCount(2)
- .parameterSuperOf(Long.TYPE, 0)
- .parameterSuperOf(byte[].class, 1)
- .build())
- .build();
-
- FuzzyFieldContract fuzzyFieldContract = FuzzyFieldContract.newBuilder()
- .typeMatches(getMinecraftObjectMatcher().and(signatureContract))
- .build();
-
- Class> signatureClass = FuzzyReflection.fromClass(messageSigClass, true)
- .getField(fuzzyFieldContract)
- .getType();
- return setMinecraftClass("SaltedSignature", signatureClass);
- }
- }
-
- public static Class> getProfilePublicKeyDataClass() {
- return getProfilePublicKeyClass().getClasses()[0];
- }
-
- public static Class> getFastUtilClass(String className) {
- return getLibraryClass("it.unimi.dsi.fastutil." + className);
- }
-
- public static Class> getInt2ObjectMapClass() {
- return getFastUtilClass("ints.Int2ObjectMap");
- }
-
- public static Class> getIntArrayListClass() {
- return getFastUtilClass("ints.IntArrayList");
- }
-
- public static Class> getLibraryClass(String classname) {
- try {
- return getMinecraftLibraryClass(classname);
- } catch (RuntimeException ex) {
- Class> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
- setMinecraftLibraryClass(classname, clazz);
- return clazz;
- }
- }
-
- /**
- * Fallback method that can determine the MinecraftServer and the ServerConfigurationManager.
- */
- private static void useFallbackServer() {
- // Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
- Constructor> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
- .getConstructor(FuzzyMethodContract.newBuilder()
- .parameterMatches(getMinecraftObjectMatcher(), 0)
- .parameterCount(2)
- .build());
- Class>[] params = selected.getParameterTypes();
-
- // Jackpot - two classes at the same time!
- setMinecraftClass("MinecraftServer", params[0]);
- setMinecraftClass("PlayerList", params[1]);
- }
-
- public static Class> getLevelChunkPacketDataClass() {
- return getNullableNMS("network.protocol.game.ClientboundLevelChunkPacketData");
- }
-
- public static Class> getLightUpdatePacketDataClass() {
- return getNullableNMS("network.protocol.game.ClientboundLightUpdatePacketData");
- }
-
- public static Class> getBlockEntityTypeClass() {
- return getMinecraftClass("world.level.block.entity.BlockEntityType", "world.level.block.entity.TileEntityTypes", "TileEntityTypes");
- }
-
- public static Class> getBlockEntityInfoClass() {
- try {
- return getMinecraftClass("BlockEntityInfo");
- } catch (RuntimeException expected) {
- Class> infoClass = (Class>) ((ParameterizedType) FuzzyReflection.fromClass(getLevelChunkPacketDataClass(),
- true).getFieldListByType(List.class).get(0).getGenericType()).getActualTypeArguments()[0];
-
- setMinecraftClass("BlockEntityInfo", infoClass);
-
- return infoClass;
- }
- }
-}
+/*
+ * 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.utility;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.ProtocolLogger;
+import com.comphenix.protocol.injector.BukkitUnwrapper;
+import com.comphenix.protocol.reflect.FuzzyReflection;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.FieldAccessor;
+import com.comphenix.protocol.reflect.accessors.MethodAccessor;
+import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
+import com.comphenix.protocol.wrappers.EnumWrappers;
+import io.netty.buffer.Unpooled;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+/**
+ * Methods and constants specifically used in conjuction with reflecting Minecraft object.
+ *
+ * @author Kristian
+ */
+public final class MinecraftReflection {
+
+ private static final ClassSource CLASS_SOURCE = ClassSource.fromClassLoader();
+
+ /**
+ * Regular expression that matches a canonical Java class.
+ */
+ private static final String CANONICAL_REGEX = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)+\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
+ private static final String MINECRAFT_CLASS_NAME_REGEX = "net\\.minecraft\\." + CANONICAL_REGEX;
+
+ /**
+ * Represents a regular expression that will match the version string in a package: org.bukkit.craftbukkit.v1_6_R2 ->
+ * v1_6_R2
+ */
+ private static final Pattern PACKAGE_VERSION_MATCHER = Pattern.compile(".*\\.(v\\d+_\\d+_\\w*\\d+)");
+
+ // Cache of getBukkitEntity
+ private static final Map, MethodAccessor> BUKKIT_ENTITY_CACHE = new HashMap<>();
+
+ /**
+ * The Entity package in Forge 1.5.2
+ */
+ private static final String FORGE_ENTITY_PACKAGE = "net.minecraft.entity";
+
+ // Package private for the purpose of unit testing
+ static CachedPackage minecraftPackage;
+ static CachedPackage craftbukkitPackage;
+ static CachedPackage libraryPackage;
+
+ /**
+ * Regular expression computed dynamically.
+ */
+ private static String DYNAMIC_PACKAGE_MATCHER = null;
+ /**
+ * The package name of all the classes that belongs to the native code in Minecraft.
+ */
+ private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
+ private static String MINECRAFT_FULL_PACKAGE = null;
+ private static String CRAFTBUKKIT_PACKAGE = null;
+
+ // fuzzy matcher for minecraft class objects
+ private static AbstractFuzzyMatcher> fuzzyMatcher;
+ // The NMS version
+ private static String packageVersion;
+ // net.minecraft.server
+ private static Class> itemStackArrayClass;
+ // Whether we are using netty
+ private static Boolean cachedWatcherObject;
+
+ // ---- ItemStack conversions
+ private static Object itemStackAir = null;
+ private static Boolean nullEnforced = null;
+
+ private static MethodAccessor asNMSCopy = null;
+ private static MethodAccessor asCraftMirror = null;
+ private static MethodAccessor isEmpty = null;
+
+ private MinecraftReflection() {
+ // No need to make this constructable.
+ }
+
+ /**
+ * Retrieve a regular expression that can match Minecraft package objects.
+ *
+ * @return Minecraft package matcher.
+ */
+ public static String getMinecraftObjectRegex() {
+ if (DYNAMIC_PACKAGE_MATCHER == null) {
+ getMinecraftPackage();
+ }
+ return DYNAMIC_PACKAGE_MATCHER;
+ }
+
+ /**
+ * Retrieve a abstract fuzzy class matcher for Minecraft objects.
+ *
+ * @return A matcher for Minecraft objects.
+ */
+ public static AbstractFuzzyMatcher> getMinecraftObjectMatcher() {
+ if (fuzzyMatcher == null) {
+ fuzzyMatcher = FuzzyMatchers.matchRegex(getMinecraftObjectRegex());
+ }
+ return fuzzyMatcher;
+ }
+
+ /**
+ * Retrieve the name of the Minecraft server package.
+ *
+ * @return Full canonical name of the Minecraft server package.
+ */
+ public static String getMinecraftPackage() {
+ // Speed things up
+ if (MINECRAFT_FULL_PACKAGE != null) {
+ return MINECRAFT_FULL_PACKAGE;
+ }
+
+ try {
+ // get the bukkit version we're running on
+ Server craftServer = Bukkit.getServer();
+ CRAFTBUKKIT_PACKAGE = craftServer.getClass().getPackage().getName();
+
+ // Parse the package version
+ Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
+ if (packageMatcher.matches()) {
+ packageVersion = packageMatcher.group(1);
+ } else {
+ MinecraftVersion version = new MinecraftVersion(craftServer);
+
+ // Just assume R1 - it's probably fine (warn anyway)
+ packageVersion = "v" + version.getMajor() + "_" + version.getMinor() + "_R1";
+ ProtocolLogger.log(Level.SEVERE, "Assuming package version: " + packageVersion);
+ }
+
+ if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
+ // total rework of the NMS structure in 1.17 (at least there's no versioning)
+ MINECRAFT_FULL_PACKAGE = MINECRAFT_PREFIX_PACKAGE = "net.minecraft";
+ setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
+ } else {
+ // extract the server version from the return type of "getHandle" in CraftEntity
+ Method getHandle = getCraftEntityClass().getMethod("getHandle");
+ MINECRAFT_FULL_PACKAGE = getHandle.getReturnType().getPackage().getName();
+
+ // Pretty important invariant
+ if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
+ // See if we got the Forge entity package
+ if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
+ // Use the standard NMS versioned package
+ MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
+ } else {
+ // Assume they're the same instead
+ MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
+ }
+
+ // The package is usually flat, so go with that assumption
+ String matcher =
+ (MINECRAFT_PREFIX_PACKAGE.length() > 0 ? Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + CANONICAL_REGEX;
+
+ // We'll still accept the default location, however
+ setDynamicPackageMatcher("(" + matcher + ")|(" + MINECRAFT_CLASS_NAME_REGEX + ")");
+
+ } else {
+ // Use the standard matcher
+ setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
+ }
+ }
+
+ return MINECRAFT_FULL_PACKAGE;
+ } catch (NoSuchMethodException exception) {
+ throw new IllegalStateException("Cannot find getHandle() in CraftEntity", exception);
+ }
+ }
+
+ /**
+ * Retrieve the package version of the underlying CraftBukkit server.
+ *
+ * @return The craftbukkit package version.
+ */
+ public static String getPackageVersion() {
+ getMinecraftPackage();
+ return packageVersion;
+ }
+
+ /**
+ * Update the dynamic package matcher.
+ *
+ * @param regex - the Minecraft package regex.
+ */
+ private static void setDynamicPackageMatcher(String regex) {
+ DYNAMIC_PACKAGE_MATCHER = regex;
+ // Ensure that the matcher is regenerated
+ fuzzyMatcher = null;
+ }
+
+ /**
+ * Used during debugging and testing.
+ *
+ * @param minecraftPackage - the current Minecraft package.
+ * @param craftBukkitPackage - the current CraftBukkit package.
+ */
+ static void setMinecraftPackage(String minecraftPackage, String craftBukkitPackage) {
+ MINECRAFT_FULL_PACKAGE = minecraftPackage;
+ CRAFTBUKKIT_PACKAGE = craftBukkitPackage;
+
+ // Make sure it exists
+ if (getMinecraftServerClass() == null) {
+ throw new IllegalArgumentException("Cannot find MinecraftServer for package " + minecraftPackage);
+ }
+
+ // Standard matcher
+ setDynamicPackageMatcher(MINECRAFT_CLASS_NAME_REGEX);
+ }
+
+ /**
+ * Retrieve the name of the root CraftBukkit package.
+ *
+ * @return Full canonical name of the root CraftBukkit package.
+ */
+ public static String getCraftBukkitPackage() {
+ // Ensure it has been initialized
+ if (CRAFTBUKKIT_PACKAGE == null) {
+ getMinecraftPackage();
+ }
+
+ return CRAFTBUKKIT_PACKAGE;
+ }
+
+ /**
+ * Dynamically retrieve the Bukkit entity from a given entity.
+ *
+ * @param nmsObject - the NMS entity.
+ * @return A bukkit entity.
+ * @throws RuntimeException If we were unable to retrieve the Bukkit entity.
+ */
+ public static Object getBukkitEntity(Object nmsObject) {
+ if (nmsObject == null) {
+ return null;
+ }
+
+ // We will have to do this dynamically, unfortunately
+ try {
+ Class> clazz = nmsObject.getClass();
+ MethodAccessor accessor = BUKKIT_ENTITY_CACHE.get(clazz);
+
+ if (accessor == null) {
+ MethodAccessor created = Accessors.getMethodAccessor(clazz, "getBukkitEntity");
+ accessor = BUKKIT_ENTITY_CACHE.putIfAbsent(clazz, created);
+
+ // We won the race
+ if (accessor == null) {
+ accessor = created;
+ }
+ }
+
+ return accessor.invoke(nmsObject);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot get Bukkit entity from " + nmsObject, e);
+ }
+ }
+
+ /**
+ * Retrieve the Bukkit player from a given PlayerConnection.
+ *
+ * @param playerConnection The PlayerConnection.
+ * @return A bukkit player.
+ * @throws RuntimeException If we were unable to retrieve the Bukkit player.
+ */
+ public static Player getBukkitPlayerFromConnection(Object playerConnection) {
+ try {
+ return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e);
+ }
+ }
+
+ /**
+ * Determine if a given object can be found within the package net.minecraft.server.
+ *
+ * @param obj - the object to test.
+ * @return TRUE if it can, FALSE otherwise.
+ */
+ public static boolean isMinecraftObject(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ // Doesn't matter if we don't check for the version here
+ return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE);
+ }
+
+ /**
+ * Determine if the given class is found within the package net.minecraft.server, or any equivalent package.
+ *
+ * @param clazz - the class to test.
+ * @return TRUE if it can, FALSE otherwise.
+ */
+ public static boolean isMinecraftClass(Class> clazz) {
+ if (clazz == null) {
+ throw new IllegalArgumentException("clazz cannot be NULL.");
+ }
+
+ return getMinecraftObjectMatcher().isMatch(clazz, null);
+ }
+
+ /**
+ * Determine if a given object is found in net.minecraft.server, and has the given name.
+ *
+ * @param obj - the object to test.
+ * @param className - the class name to test.
+ * @return TRUE if it can, FALSE otherwise.
+ */
+ public static boolean isMinecraftObject(Object obj, String className) {
+ if (obj == null) {
+ return false;
+ }
+
+ String javaName = obj.getClass().getName();
+ return javaName.startsWith(MINECRAFT_PREFIX_PACKAGE) && javaName.endsWith(className);
+ }
+
+ /**
+ * Determine if a given Object is compatible with a given Class. That is, whether or not the Object is an instance of
+ * that Class or one of its subclasses. If either is null, false is returned.
+ *
+ * @param clazz Class to test for, may be null
+ * @param object the Object to test, may be null
+ * @return True if it is, false if not
+ * @see Class#isAssignableFrom(Class)
+ */
+ public static boolean is(Class> clazz, Object object) {
+ if (clazz == null || object == null) {
+ return false;
+ }
+
+ return clazz.isAssignableFrom(object.getClass());
+ }
+
+ /**
+ * Equivalent to {@link #is(Class, Object)} but we don't call getClass again
+ */
+ public static boolean is(Class> clazz, Class> test) {
+ if (clazz == null || test == null) {
+ return false;
+ }
+
+ return clazz.isAssignableFrom(test);
+ }
+
+ /**
+ * Determine if a given object is a BlockPosition.
+ *
+ * @param obj - the object to test.
+ * @return TRUE if it can, FALSE otherwise.
+ */
+ public static boolean isBlockPosition(Object obj) {
+ return is(getBlockPositionClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is an NMS ChunkCoordIntPar.
+ *
+ * @param obj - the object.
+ * @return TRUE if it can, FALSE otherwise.
+ */
+ public static boolean isChunkCoordIntPair(Object obj) {
+ return is(getChunkCoordIntPair(), obj);
+ }
+
+ /**
+ * Determine if the given object is actually a Minecraft packet.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isPacketClass(Object obj) {
+ return is(getPacketClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is assignable to a NetServerHandler (PlayerConnection)
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isServerHandler(Object obj) {
+ return is(getPlayerConnectionClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is actually a Minecraft packet.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isMinecraftEntity(Object obj) {
+ return is(getEntityClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is a NMS ItemStack.
+ *
+ * @param value - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isItemStack(Object value) {
+ return is(getItemStackClass(), value);
+ }
+
+ /**
+ * Determine if the given object is a CraftPlayer class.
+ *
+ * @param value - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isCraftPlayer(Object value) {
+ return is(getCraftPlayerClass(), value);
+ }
+
+ /**
+ * Determine if the given object is a Minecraft player entity.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isMinecraftPlayer(Object obj) {
+ return is(getEntityPlayerClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is a data watcher object.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isDataWatcher(Object obj) {
+ return is(getDataWatcherClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is an IntHashMap object.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isIntHashMap(Object obj) {
+ return is(getIntHashMapClass(), obj);
+ }
+
+ /**
+ * Determine if the given object is a CraftItemStack instancey.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isCraftItemStack(Object obj) {
+ return is(getCraftItemStackClass(), obj);
+ }
+
+ /**
+ * Retrieve the EntityPlayer (NMS) class.
+ *
+ * @return The entity class.
+ */
+ public static Class> getEntityPlayerClass() {
+ try {
+ return getMinecraftClass("server.level.EntityPlayer", "server.level.ServerPlayer", "EntityPlayer");
+ } catch (RuntimeException e) {
+ try {
+ // Grab CraftPlayer's handle
+ Method getHandle = FuzzyReflection
+ .fromClass(getCraftBukkitClass("entity.CraftPlayer"))
+ .getMethodByName("getHandle");
+
+ // EntityPlayer is the return type
+ return setMinecraftClass("EntityPlayer", getHandle.getReturnType());
+ } catch (IllegalArgumentException e1) {
+ throw new RuntimeException("Could not find EntityPlayer class.", e1);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the EntityHuman class.
+ *
+ * @return The entity human class.
+ */
+ public static Class> getEntityHumanClass() {
+ // Assume its the direct superclass
+ return getEntityPlayerClass().getSuperclass();
+ }
+
+ /**
+ * Retrieve the GameProfile class.
+ *
+ * @return The game profile class.
+ */
+ public static Class> getGameProfileClass() {
+ return getClass("com.mojang.authlib.GameProfile");
+ }
+
+ /**
+ * Retrieve the entity (NMS) class.
+ *
+ * @return The entity class.
+ */
+ public static Class> getEntityClass() {
+ try {
+ return getMinecraftClass("world.entity.Entity", "Entity");
+ } catch (RuntimeException e) {
+ return fallbackMethodReturn("Entity", "entity.CraftEntity", "getHandle");
+ }
+ }
+
+ /**
+ * Retrieve the CraftChatMessage.
+ *
+ * @return The CraftChatMessage class.
+ */
+ public static Class> getCraftChatMessage() {
+ return getCraftBukkitClass("util.CraftChatMessage");
+ }
+
+ /**
+ * Retrieve the WorldServer (NMS) class.
+ *
+ * @return The WorldServer class.
+ */
+ public static Class> getWorldServerClass() {
+ try {
+ return getMinecraftClass("server.level.WorldServer", "server.level.ServerLevel", "WorldServer");
+ } catch (RuntimeException e) {
+ return fallbackMethodReturn("WorldServer", "CraftWorld", "getHandle");
+ }
+ }
+
+ /**
+ * Retrieve the World (NMS) class.
+ *
+ * @return The world class.
+ */
+ public static Class> getNmsWorldClass() {
+ try {
+ return getMinecraftClass("world.level.World", "world.level.Level", "World");
+ } catch (RuntimeException e) {
+ return setMinecraftClass("World", getWorldServerClass().getSuperclass());
+ }
+ }
+
+ /**
+ * Fallback on the return value of a named method in order to get a NMS class.
+ *
+ * @param nmsClass - the expected name of the Minecraft class.
+ * @param craftClass - a CraftBukkit class to look at.
+ * @param methodName - the method we will use.
+ * @return The return value of this method, which will be saved to the package cache.
+ */
+ private static Class> fallbackMethodReturn(String nmsClass, String craftClass, String methodName) {
+ Class> result = FuzzyReflection.fromClass(getCraftBukkitClass(craftClass))
+ .getMethodByName(methodName)
+ .getReturnType();
+ // Save the result
+ return setMinecraftClass(nmsClass, result);
+ }
+
+ /**
+ * Retrieve the packet class.
+ *
+ * @return The packet class.
+ */
+ public static Class> getPacketClass() {
+ return getMinecraftClass("network.protocol.Packet", "Packet");
+ }
+
+ public static Class> getByteBufClass() {
+ return getClass("io.netty.buffer.ByteBuf");
+ }
+
+ /**
+ * Retrieve the EnumProtocol class.
+ *
+ * @return The Enum protocol class.
+ */
+ public static Class> getEnumProtocolClass() {
+ return getMinecraftClass("network.EnumProtocol", "network.ConnectionProtocol", "EnumProtocol");
+ }
+
+ /**
+ * Retrieve the IChatBaseComponent class.
+ *
+ * @return The IChatBaseComponent.
+ */
+ public static Class> getIChatBaseComponentClass() {
+ return getMinecraftClass("network.chat.IChatBaseComponent", "network.chat.IChatbaseComponent", "network.chat.Component", "IChatBaseComponent");
+ }
+
+ public static Class> getIChatBaseComponentArrayClass() {
+ return getArrayClass(getIChatBaseComponentClass());
+ }
+
+ /**
+ * Retrieve the NMS chat component text class.
+ *
+ * @return The chat component class.
+ */
+ public static Class> getChatComponentTextClass() {
+ return getMinecraftClass("network.chat.ChatComponentText", "network.chat.TextComponent", "ChatComponentText");
+ }
+
+ /**
+ * Attempt to find the ChatSerializer class.
+ *
+ * @return The serializer class.
+ * @throws IllegalStateException If the class could not be found or deduced.
+ */
+ public static Class> getChatSerializerClass() {
+ return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
+ }
+
+ /**
+ * Retrieve the ServerPing class.
+ *
+ * @return The ServerPing class.
+ */
+ public static Class> getServerPingClass() {
+ return getMinecraftClass("network.protocol.status.ServerPing", "network.protocol.status.ServerStatus", "ServerPing");
+ }
+
+ /**
+ * Retrieve the ServerPingServerData class.
+ *
+ * @return The ServerPingServerData class.
+ */
+ public static Class> getServerPingServerDataClass() {
+ return getMinecraftClass("network.protocol.status.ServerPing$ServerData", "network.protocol.status.ServerStatus$Version", "ServerPing$ServerData");
+ }
+
+ /**
+ * Retrieve the ServerPingPlayerSample class.
+ *
+ * @return The ServerPingPlayerSample class.
+ */
+ public static Class> getServerPingPlayerSampleClass() {
+ return getMinecraftClass(
+ "network.protocol.status.ServerPing$ServerPingPlayerSample",
+ "network.protocol.status.ServerStatus$Players",
+ "ServerPing$ServerPingPlayerSample");
+ }
+
+ /**
+ * Retrieve the MinecraftServer class.
+ *
+ * @return MinecraftServer class.
+ */
+ public static Class> getMinecraftServerClass() {
+ try {
+ return getMinecraftClass("server.MinecraftServer", "MinecraftServer");
+ } catch (RuntimeException e) {
+ // Reset cache and try again
+ setMinecraftClass("MinecraftServer", null);
+
+ useFallbackServer();
+ return getMinecraftClass("MinecraftServer");
+ }
+ }
+
+ /**
+ * Retrieve the NMS statistics class.
+ *
+ * @return The statistics class.
+ */
+ public static Class> getStatisticClass() {
+ return getMinecraftClass("stats.Statistic", "stats.Stat", "Statistic");
+ }
+
+ /**
+ * Retrieve the NMS statistic list class.
+ *
+ * @return The statistic list class.
+ */
+ public static Class> getStatisticListClass() {
+ return getMinecraftClass("stats.StatisticList", "stats.Stats", "StatisticList");
+ }
+
+ /**
+ * Retrieve the player list class (or ServerConfigurationManager),
+ *
+ * @return The player list class.
+ */
+ public static Class> getPlayerListClass() {
+ try {
+ return getMinecraftClass("server.players.PlayerList", "PlayerList");
+ } catch (RuntimeException e) {
+ // Reset cache and try again
+ setMinecraftClass("PlayerList", null);
+
+ useFallbackServer();
+ return getMinecraftClass("PlayerList");
+ }
+ }
+
+ /**
+ * Retrieve the PlayerConnection class.
+ *
+ * @return The PlayerConnection class.
+ */
+ public static Class> getPlayerConnectionClass() {
+ return getMinecraftClass("server.network.PlayerConnection", "server.network.ServerGamePacketListenerImpl", "PlayerConnection");
+ }
+
+ /**
+ * Retrieve the NetworkManager class.
+ *
+ * @return The NetworkManager class.
+ */
+ public static Class> getNetworkManagerClass() {
+ return getMinecraftClass("network.NetworkManager", "network.Connection", "NetworkManager");
+ }
+
+ /**
+ * Retrieve the NMS ItemStack class.
+ *
+ * @return The ItemStack class.
+ */
+ public static Class> getItemStackClass() {
+ try {
+ return getMinecraftClass("world.item.ItemStack", "ItemStack");
+ } catch (RuntimeException e) {
+ // Use the handle reference
+ return setMinecraftClass("ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
+ .getFieldByName("handle")
+ .getType());
+ }
+ }
+
+ /**
+ * Retrieve the Block (NMS) class.
+ *
+ * @return Block (NMS) class.
+ */
+ public static Class> getBlockClass() {
+ return getMinecraftClass("world.level.block.Block", "Block");
+ }
+
+ public static Class> getItemClass() {
+ return getNullableNMS("world.item.Item", "Item");
+ }
+
+ public static Class> getFluidTypeClass() {
+ return getNullableNMS("world.level.material.FluidType", "world.level.material.Fluid", "FluidType");
+ }
+
+ public static Class> getParticleTypeClass() {
+ return getNullableNMS("core.particles.ParticleType", "core.particles.SimpleParticleType", "ParticleType");
+ }
+
+ /**
+ * Retrieve the WorldType class.
+ *
+ * @return The WorldType class.
+ */
+ public static Class> getWorldTypeClass() {
+ return getMinecraftClass("WorldType");
+ }
+
+ /**
+ * Retrieve the DataWatcher class.
+ *
+ * @return The DataWatcher class.
+ */
+ public static Class> getDataWatcherClass() {
+ return getMinecraftClass("network.syncher.DataWatcher", "network.syncher.SynchedEntityData", "DataWatcher");
+ }
+
+ /**
+ * Retrieves the BlockPosition class.
+ *
+ * @return The BlockPosition class.
+ */
+ public static Class> getBlockPositionClass() {
+ return getMinecraftClass("core.BlockPosition", "core.BlockPos", "BlockPosition");
+ }
+
+ /**
+ * Retrieves the Vec3D class.
+ *
+ * @return The Vec3D class.
+ */
+ public static Class> getVec3DClass() {
+ return getMinecraftClass("world.phys.Vec3D", "world.phys.Vec3", "Vec3D");
+ }
+
+ /**
+ * Retrieve the ChunkCoordIntPair class.
+ *
+ * @return The ChunkCoordIntPair class.
+ */
+ public static Class> getChunkCoordIntPair() {
+ return getMinecraftClass("world.level.ChunkCoordIntPair", "world.level.ChunkPos", "ChunkCoordIntPair");
+ }
+
+ /**
+ * Retrieve the DataWatcher Item class.
+ *
+ * @return The class
+ */
+ public static Class> getDataWatcherItemClass() {
+ return getMinecraftClass("network.syncher.DataWatcher$Item", "network.syncher.SynchedEntityData$DataItem", "DataWatcher$Item", "DataWatcher$WatchableObject");
+ }
+
+ public static Class> getDataWatcherObjectClass() {
+ return getNullableNMS("network.syncher.DataWatcherObject", "network.syncher.EntityDataAccessor", "DataWatcherObject");
+ }
+
+ public static boolean watcherObjectExists() {
+ if (cachedWatcherObject == null) {
+ cachedWatcherObject = getDataWatcherObjectClass() != null;
+ }
+
+ return cachedWatcherObject;
+ }
+
+ public static Class> getDataWatcherSerializerClass() {
+ return getNullableNMS("network.syncher.DataWatcherSerializer", "network.syncher.EntityDataSerializer", "DataWatcherSerializer");
+ }
+
+ public static Class> getDataWatcherRegistryClass() {
+ return getMinecraftClass("network.syncher.DataWatcherRegistry", "network.syncher.EntityDataSerializers", "DataWatcherRegistry");
+ }
+
+ public static Class> getMinecraftKeyClass() {
+ return getMinecraftClass("resources.MinecraftKey", "resources.ResourceLocation", "MinecraftKey");
+ }
+
+ public static Class> getMobEffectListClass() {
+ return getMinecraftClass("world.effect.MobEffectList", "MobEffectList", "world.effect.MobEffect");
+ }
+
+ public static Class> getSoundEffectClass() {
+ return getNullableNMS("sounds.SoundEffect", "sounds.SoundEvent", "SoundEffect");
+ }
+
+ /**
+ * Retrieve the ServerConnection abstract class.
+ *
+ * @return The ServerConnection class.
+ */
+ public static Class> getServerConnectionClass() {
+ return getMinecraftClass("server.network.ServerConnection", "server.network.ServerConnectionListener", "ServerConnection");
+ }
+
+ /**
+ * Retrieve the NBT base class.
+ *
+ * @return The NBT base class.
+ */
+ public static Class> getNBTBaseClass() {
+ return getMinecraftClass("nbt.NBTBase", "nbt.Tag", "NBTBase");
+ }
+
+ /**
+ * Retrieve the NBT read limiter class.
+ *
+ * @return The NBT read limiter.
+ */
+ public static Class> getNBTReadLimiterClass() {
+ return getMinecraftClass("nbt.NBTReadLimiter", "nbt.NbtAccounter", "NBTReadLimiter");
+ }
+
+ /**
+ * Retrieve the NBT Compound class.
+ *
+ * @return The NBT Compond class.
+ */
+ public static Class> getNBTCompoundClass() {
+ return getMinecraftClass("nbt.NBTTagCompound", "nbt.CompoundTag", "NBTTagCompound");
+ }
+
+ /**
+ * Retrieve the EntityTracker (NMS) class.
+ *
+ * @return EntityTracker class.
+ */
+ public static Class> getEntityTrackerClass() {
+ return getMinecraftClass("server.level.PlayerChunkMap$EntityTracker", "server.level.ChunkMap$TrackedEntity", "EntityTracker");
+ }
+
+ /**
+ * Retrieve the attribute snapshot class.
+ *
+ * This stores the final value of an attribute, along with all the associated computational steps.
+ *
+ * @return The attribute snapshot class.
+ */
+ public static Class> getAttributeSnapshotClass() {
+ return getMinecraftClass(
+ "network.protocol.game.PacketPlayOutUpdateAttributes$AttributeSnapshot",
+ "network.protocol.game.ClientboundUpdateAttributesPacket$AttributeSnapshot",
+ "AttributeSnapshot",
+ "PacketPlayOutUpdateAttributes$AttributeSnapshot");
+ }
+
+ /**
+ * Retrieve the IntHashMap class.
+ *
+ * @return IntHashMap class.
+ */
+ public static Class> getIntHashMapClass() {
+ return getNullableNMS("IntHashMap");
+ }
+
+ /**
+ * Retrieve the attribute modifier class.
+ *
+ * @return Attribute modifier class.
+ */
+ public static Class> getAttributeModifierClass() {
+ return getMinecraftClass("world.entity.ai.attributes.AttributeModifier", "AttributeModifier");
+ }
+
+ /**
+ * Retrieve the net.minecraft.server.MobEffect class.
+ *
+ * @return The mob effect class.
+ */
+ public static Class> getMobEffectClass() {
+ return getMinecraftClass("world.effect.MobEffect", "world.effect.MobEffectInstance", "MobEffect");
+ }
+
+ /**
+ * Retrieve the packet data serializer class that overrides ByteBuf.
+ *
+ * @return The data serializer class.
+ */
+ public static Class> getPacketDataSerializerClass() {
+ return getMinecraftClass("network.PacketDataSerializer", "network.FriendlyByteBuf", "PacketDataSerializer");
+ }
+
+ /**
+ * Retrieve the NBTCompressedStreamTools class.
+ *
+ * @return The NBTCompressedStreamTools class.
+ */
+ public static Class> getNbtCompressedStreamToolsClass() {
+ return getMinecraftClass("nbt.NBTCompressedStreamTools", "nbt.NbtIo", "NBTCompressedStreamTools");
+ }
+
+ /**
+ * Retrieve the NMS tile entity class.
+ *
+ * @return The tile entity class.
+ */
+ public static Class> getTileEntityClass() {
+ return getMinecraftClass("world.level.block.entity.TileEntity", "world.level.block.entity.BlockEntity", "TileEntity");
+ }
+
+ /**
+ * Retrieve the Gson class used by Minecraft.
+ *
+ * @return The Gson class.
+ */
+ public static Class> getMinecraftGsonClass() {
+ return getMinecraftLibraryClass("com.google.gson.Gson");
+ }
+
+ /**
+ * Retrieve the ItemStack[] class.
+ *
+ * @return The ItemStack[] class.
+ */
+ public static Class> getItemStackArrayClass() {
+ if (itemStackArrayClass == null) {
+ itemStackArrayClass = getArrayClass(getItemStackClass());
+ }
+ return itemStackArrayClass;
+ }
+
+ /**
+ * Retrieve the array class of a given component type.
+ *
+ * @param componentType - type of each element in the array.
+ * @return The class of the array.
+ */
+ public static Class> getArrayClass(Class> componentType) {
+ // A bit of a hack, but it works
+ return Array.newInstance(componentType, 0).getClass();
+ }
+
+ /**
+ * Retrieve the CraftItemStack class.
+ *
+ * @return The CraftItemStack class.
+ */
+ public static Class> getCraftItemStackClass() {
+ return getCraftBukkitClass("inventory.CraftItemStack");
+ }
+
+ /**
+ * Retrieve the CraftPlayer class.
+ *
+ * @return CraftPlayer class.
+ */
+ public static Class> getCraftPlayerClass() {
+ return getCraftBukkitClass("entity.CraftPlayer");
+ }
+
+ /**
+ * Retrieve the CraftWorld class.
+ *
+ * @return The CraftWorld class.
+ */
+ public static Class> getCraftWorldClass() {
+ return getCraftBukkitClass("CraftWorld");
+ }
+
+ /**
+ * Retrieve the CraftEntity class.
+ *
+ * @return CraftEntity class.
+ */
+ public static Class> getCraftEntityClass() {
+ return getCraftBukkitClass("entity.CraftEntity");
+ }
+
+ /**
+ * Retrieve the CraftChatMessage introduced in 1.7.2
+ *
+ * @return The CraftChatMessage class.
+ */
+ public static Class> getCraftMessageClass() {
+ return getCraftBukkitClass("util.CraftChatMessage");
+ }
+
+ /**
+ * Retrieve the PlayerInfoData class in 1.8.
+ *
+ * @return The PlayerInfoData class
+ */
+ public static Class> getPlayerInfoDataClass() {
+ return getMinecraftClass(
+ "network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
+ "network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
+ "PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
+ }
+
+ /**
+ * Retrieves the entity use action class in 1.17.
+ *
+ * @return The EntityUseAction class
+ */
+ public static Class> getEnumEntityUseActionClass() {
+ Class> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
+ FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
+ try {
+ return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
+ } catch (IllegalArgumentException ignored) {
+ return fuzzyReflection.getFieldByType("^.*(Action)").getType();
+ }
+ }
+
+ /**
+ * Get a method accessor to get the actual use action out of the wrapping EnumEntityUseAction in 1.17.
+ *
+ * @return a method accessor to get the actual use action
+ */
+ public static MethodAccessor getEntityUseActionEnumMethodAccessor() {
+ FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
+ return Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
+ .returnTypeExact(EnumWrappers.getEntityUseActionClass())
+ .build()));
+ }
+
+ /**
+ * Get a field accessor for the hand in the wrapping EnumEntityUseAction in 1.17.
+ *
+ * @param enumEntityUseAction the object instance of the action, the field is not present in attack.
+ * @return a field accessor for the hand in the wrapping EnumEntityUseAction
+ */
+ public static FieldAccessor getHandEntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
+ FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
+ return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
+ .typeExact(EnumWrappers.getHandClass())
+ .build()));
+ }
+
+ /**
+ * Get a field accessor for the vec3d in the wrapping EnumEntityUseAction in 1.17.
+ *
+ * @param enumEntityUseAction the object instance of the action, the field is not present in attack.
+ * @return a field accessor for the hand in the wrapping EnumEntityUseAction
+ */
+ public static FieldAccessor getVec3EntityUseActionEnumFieldAccessor(Object enumEntityUseAction) {
+ FuzzyReflection fuzzy = FuzzyReflection.fromObject(enumEntityUseAction, true);
+ return Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
+ .typeExact(MinecraftReflection.getVec3DClass())
+ .build()));
+ }
+
+ /**
+ * Determine if the given object is a PlayerInfoData.
+ *
+ * @param obj - the given object.
+ * @return TRUE if it is, FALSE otherwise.
+ */
+ public static boolean isPlayerInfoData(Object obj) {
+ return is(getPlayerInfoDataClass(), obj);
+ }
+
+ /**
+ * Retrieve the IBlockData class in 1.8.
+ *
+ * @return The IBlockData class
+ */
+ public static Class> getIBlockDataClass() {
+ return getMinecraftClass("world.level.block.state.IBlockData", "world.level.block.state.BlockState", "IBlockData");
+ }
+
+ /**
+ * Retrieve the MultiBlockChangeInfo class in 1.8.
+ *
+ * @return The MultiBlockChangeInfo class
+ */
+ public static Class> getMultiBlockChangeInfoClass() {
+ return getMinecraftClass("MultiBlockChangeInfo", "PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
+ }
+
+ /**
+ * Retrieve the MultiBlockChangeInfo array class in 1.8.
+ *
+ * @return The MultiBlockChangeInfo array class
+ */
+ public static Class> getMultiBlockChangeInfoArrayClass() {
+ return getArrayClass(getMultiBlockChangeInfoClass());
+ }
+
+ /**
+ * Retrieve the PacketPlayOutGameStateChange.a class, aka GameState in 1.16
+ *
+ * @return The GameState class
+ */
+ public static Class> getGameStateClass() {
+ // it's called "a" so there's not a lot we can do to identify it
+ Class> packetClass = PacketType.Play.Server.GAME_STATE_CHANGE.getPacketClass();
+ return packetClass.getClasses()[0];
+ }
+
+ public static boolean signUpdateExists() {
+ return getNullableNMS("PacketPlayOutUpdateSign") != null;
+ }
+
+ public static Class> getNonNullListClass() {
+ return getMinecraftClass("core.NonNullList", "NonNullList");
+ }
+
+ public static MethodAccessor getNonNullListCreateAccessor() {
+ try {
+ Class> nonNullListType = MinecraftReflection.getNonNullListClass();
+ Method method = FuzzyReflection.fromClass(nonNullListType).getMethod(FuzzyMethodContract.newBuilder()
+ .returnTypeExact(nonNullListType)
+ .requireModifier(Modifier.STATIC)
+ .build());
+ return Accessors.getMethodAccessor(method);
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ public static Class> getCraftSoundClass() {
+ return getCraftBukkitClass("CraftSound");
+ }
+
+ public static Class> getSectionPositionClass() {
+ return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
+ }
+
+ /**
+ * Retrieves the Bukkit equivalent of a NMS ItemStack. This method should preserve NBT data and will never return null
+ * when supplied with a valid ItemStack. Empty ItemStacks are treated as AIR.
+ *
+ * @param generic NMS ItemStack
+ * @return The Bukkit equivalent
+ */
+ public static ItemStack getBukkitItemStack(Object generic) {
+ if (generic == null) {
+ // Convert null to AIR - 1.11 behavior
+ return new ItemStack(Material.AIR);
+ }
+
+ if (generic instanceof ItemStack) {
+ ItemStack bukkit = (ItemStack) generic;
+
+ // They're probably looking for the CraftItemStack
+ // If it's one already our work is done
+ if (is(getCraftItemStackClass(), generic)) {
+ return bukkit;
+ }
+
+ // If not, convert it to one
+ Object nmsStack = getMinecraftItemStack(bukkit);
+ return getBukkitItemStack(nmsStack);
+ }
+
+ if (!is(getItemStackClass(), generic)) {
+ // We can't do anything with non-ItemStacks
+ throw new IllegalArgumentException(generic + " is not an ItemStack!");
+ }
+
+ try {
+ // Check null enforcement - 1.11 behavior
+ if (nullEnforced == null) {
+ isEmpty = Accessors.getMethodAccessor(getItemStackClass().getMethod("isEmpty"));
+ nullEnforced = true;
+ }
+
+ if (nullEnforced) {
+ if ((boolean) isEmpty.invoke(generic)) {
+ return new ItemStack(Material.AIR);
+ }
+ }
+ } catch (ReflectiveOperationException ex) {
+ nullEnforced = false;
+ }
+
+ if (asCraftMirror == null) {
+ try {
+ Method asMirrorMethod = getCraftItemStackClass().getMethod("asCraftMirror", getItemStackClass());
+ asCraftMirror = Accessors.getMethodAccessor(asMirrorMethod);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException("Failed to obtain CraftItemStack.asCraftMirror", ex);
+ }
+ }
+
+ try {
+ // Convert to a craft mirror to preserve NBT data
+ return (ItemStack) asCraftMirror.invoke(nullEnforced, generic);
+ } catch (IllegalStateException ex) {
+ throw new RuntimeException("Failed to obtain craft mirror of " + generic, ex);
+ }
+ }
+
+ /**
+ * Retrieves the NMS equivalent of a Bukkit ItemStack. This method will never return null and should preserve NBT
+ * data. Null inputs are treated as empty (AIR) ItemStacks.
+ *
+ * @param specific Bukkit ItemStack
+ * @return The NMS equivalent
+ */
+ public static Object getMinecraftItemStack(ItemStack specific) {
+ if (asNMSCopy == null) {
+ try {
+ Method asNmsCopyMethod = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
+ asNMSCopy = Accessors.getMethodAccessor(asNmsCopyMethod);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException("Failed to obtain CraftItemStack.asNMSCopy", ex);
+ }
+ }
+
+ if (is(getCraftItemStackClass(), specific)) {
+ // If it's already a CraftItemStack, use its handle
+ Object unwrapped = BukkitUnwrapper.getInstance().unwrapItem(specific);
+ if (unwrapped != null) {
+ return unwrapped;
+ } else {
+ if (itemStackAir == null) {
+ // Easiest way to get the Material.AIR ItemStack?
+ itemStackAir = getMinecraftItemStack(new ItemStack(Material.AIR));
+ }
+ return itemStackAir;
+ }
+ }
+
+ try {
+ // If not, grab a NMS copy
+ return asNMSCopy.invoke(null, specific);
+ } catch (IllegalStateException ex) {
+ throw new RuntimeException("Failed to make NMS copy of " + specific, ex);
+ }
+ }
+
+ /**
+ * Retrieve the given class by name.
+ *
+ * @param className - name of the class.
+ * @return The class.
+ */
+ private static Class> getClass(String className) {
+ try {
+ return getClassSource().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Cannot find class " + className, e);
+ }
+ }
+
+ /**
+ * Retrieve the class object of a specific CraftBukkit class.
+ *
+ * @param className - the specific CraftBukkit class.
+ * @return Class object.
+ * @throws RuntimeException If we are unable to find the given class.
+ */
+ public static Class> getCraftBukkitClass(String className) {
+ if (craftbukkitPackage == null) {
+ craftbukkitPackage = new CachedPackage(getCraftBukkitPackage(), getClassSource());
+ }
+
+ return craftbukkitPackage.getPackageClass(className)
+ .orElseThrow(() -> new RuntimeException("Failed to find CraftBukkit class: " + className));
+ }
+
+ /**
+ * Retrieve the class object of a specific Minecraft class.
+ *
+ * @param className - the specific Minecraft class.
+ * @return Class object.
+ * @throws RuntimeException If we are unable to find the given class.
+ */
+ public static Class> getMinecraftClass(String className) {
+ if (minecraftPackage == null) {
+ minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
+ }
+
+ return minecraftPackage.getPackageClass(className)
+ .orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
+ }
+
+ public static Class> getNullableNMS(String className, String... aliases) {
+ try {
+ return getMinecraftClass(className, aliases);
+ } catch (RuntimeException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Set the class object for the specific Minecraft class.
+ *
+ * @param className - name of the Minecraft class.
+ * @param clazz - the new class object.
+ * @return The provided clazz object.
+ */
+ private static Class> setMinecraftClass(String className, Class> clazz) {
+ if (minecraftPackage == null) {
+ minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
+ }
+
+ minecraftPackage.setPackageClass(className, clazz);
+ return clazz;
+ }
+
+ /**
+ * Retrieve the current class source.
+ *
+ * @return The class source.
+ */
+ private static ClassSource getClassSource() {
+ return CLASS_SOURCE;
+ }
+
+ /**
+ * Retrieve the first class that matches a specified Minecraft name.
+ *
+ * @param className - the specific Minecraft class.
+ * @param aliases - alternative names for this Minecraft class.
+ * @return Class object.
+ * @throws RuntimeException If we are unable to find any of the given classes.
+ */
+ public static Class> getMinecraftClass(String className, String... aliases) {
+ if (minecraftPackage == null) {
+ minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
+ }
+
+ return minecraftPackage.getPackageClass(className).orElseGet(() -> {
+ Class> resolved = null;
+ for (String alias : aliases) {
+ // try to resolve the class and stop searching if we found it
+ resolved = minecraftPackage.getPackageClass(alias).orElse(null);
+ if (resolved != null) {
+ break;
+ }
+ }
+
+ // if we resolved the class cache it and return the result
+ if (resolved != null) {
+ minecraftPackage.setPackageClass(className, resolved);
+ return resolved;
+ }
+
+ // unable to find the class
+ throw new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases)));
+ });
+ }
+
+ /**
+ * Retrieve the class object of a specific Minecraft library class.
+ *
+ * @param className - the specific library Minecraft class.
+ * @return Class object.
+ * @throws RuntimeException If we are unable to find the given class.
+ */
+ public static Class> getMinecraftLibraryClass(String className) {
+ if (libraryPackage == null) {
+ libraryPackage = new CachedPackage("", getClassSource());
+ }
+
+ return libraryPackage.getPackageClass(className)
+ .orElseThrow(() -> new RuntimeException("Failed to find class: " + className));
+ }
+
+ /**
+ * Set the class object for the specific library class.
+ *
+ * @param className - name of the Minecraft library class.
+ * @param clazz - the new class object.
+ * @return The provided clazz object.
+ */
+ private static Class> setMinecraftLibraryClass(String className, Class> clazz) {
+ if (libraryPackage == null) {
+ libraryPackage = new CachedPackage("", getClassSource());
+ }
+
+ libraryPackage.setPackageClass(className, clazz);
+ return clazz;
+ }
+
+ /**
+ * Dynamically retrieve the NetworkManager name.
+ *
+ * @return Name of the NetworkManager class.
+ */
+ public static String getNetworkManagerName() {
+ return getNetworkManagerClass().getSimpleName();
+ }
+
+ /**
+ * Retrieve an instance of the packet data serializer wrapper.
+ *
+ * @param buffer - the buffer.
+ * @return The instance.
+ */
+ public static Object getPacketDataSerializer(Object buffer) {
+ try {
+ // TODO: move this to MinecraftMethods, or at least, cache the constructor accessor
+ Class> packetSerializer = getPacketDataSerializerClass();
+ return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer);
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot construct packet serializer.", e);
+ }
+ }
+
+ public static Object createPacketDataSerializer(int initialSize) {
+ // validate the initial size
+ if (initialSize <= 0) {
+ initialSize = 256;
+ }
+
+ Object buffer = Unpooled.buffer(initialSize);
+ return getPacketDataSerializer(buffer);
+ }
+
+ public static Class> getNbtTagTypes() {
+ return getMinecraftClass("nbt.NBTTagTypes", "nbt.TagTypes", "NBTTagTypes");
+ }
+
+ public static Class> getChatDeserializer() {
+ return getMinecraftClass("util.ChatDeserializer", "util.GsonHelper", "ChatDeserializer");
+ }
+
+ public static Class> getChatMutableComponentClass() {
+ return getMinecraftClass("network.chat.IChatMutableComponent", "network.chat.MutableComponent");
+ }
+
+ public static Class> getDimensionManager() {
+ return getMinecraftClass("world.level.dimension.DimensionManager", "world.level.dimension.DimensionType", "DimensionManager");
+ }
+
+ public static Class> getMerchantRecipeList() {
+ return getMinecraftClass("world.item.trading.MerchantRecipeList", "world.item.trading.MerchantOffers", "MerchantRecipeList");
+ }
+
+ public static Class> getResourceKey() {
+ return getMinecraftClass("resources.ResourceKey", "ResourceKey");
+ }
+
+ public static Class> getEntityTypes() {
+ return getMinecraftClass("world.entity.EntityTypes", "world.entity.EntityType", "EntityTypes");
+ }
+
+ public static Class> getParticleParam() {
+ return getMinecraftClass("core.particles.ParticleParam", "core.particles.ParticleOptions", "ParticleParam");
+ }
+
+ public static Class> getSectionPosition() {
+ return getMinecraftClass("core.SectionPosition", "core.SectionPos", "SectionPosition");
+ }
+
+ public static Class> getChunkProviderServer() {
+ return getMinecraftClass("server.level.ChunkProviderServer", "server.level.ServerChunkCache", "ChunkProviderServer");
+ }
+
+ public static Class> getPlayerChunkMap() {
+ return getMinecraftClass("server.level.PlayerChunkMap", "server.level.ChunkMap", "PlayerChunkMap");
+ }
+
+ public static Class> getIRegistry() {
+ return getNullableNMS("core.IRegistry", "core.Registry", "IRegistry");
+ }
+
+ public static Class> getAttributeBase() {
+ return getMinecraftClass("world.entity.ai.attributes.AttributeBase", "world.entity.ai.attributes.Attribute", "AttributeBase");
+ }
+
+ public static Class> getProfilePublicKeyClass() {
+ return getMinecraftClass("world.entity.player.ProfilePublicKey");
+ }
+
+ public static Class> getSaltedSignatureClass() {
+ try {
+ return getMinecraftClass("SaltedSignature");
+ } catch (RuntimeException runtimeException) {
+ Class> minecraftEncryption = getMinecraftClass("util.MinecraftEncryption", "util.Crypt", "MinecraftEncryption");
+ FuzzyMethodContract constructorContract = FuzzyMethodContract.newBuilder()
+ .parameterCount(2)
+ .parameterExactType(Long.TYPE, 0)
+ .parameterExactType(byte[].class, 1)
+ .build();
+
+ for (Class> subclass : minecraftEncryption.getClasses()) {
+ FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(subclass, true);
+ List> constructors = fuzzyReflection.getConstructorList(constructorContract);
+
+ if (!constructors.isEmpty()) {
+ return setMinecraftClass("SaltedSignature", subclass);
+ }
+ }
+
+ Class> messageSigClass = getMinecraftClass("network.chat.MessageSignature", "MessageSignature");
+ FuzzyClassContract signatureContract = FuzzyClassContract.newBuilder()
+ .constructor(FuzzyMethodContract.newBuilder()
+ .parameterCount(2)
+ .parameterSuperOf(Long.TYPE, 0)
+ .parameterSuperOf(byte[].class, 1)
+ .build())
+ .build();
+
+ FuzzyFieldContract fuzzyFieldContract = FuzzyFieldContract.newBuilder()
+ .typeMatches(getMinecraftObjectMatcher().and(signatureContract))
+ .build();
+
+ Class> signatureClass = FuzzyReflection.fromClass(messageSigClass, true)
+ .getField(fuzzyFieldContract)
+ .getType();
+ return setMinecraftClass("SaltedSignature", signatureClass);
+ }
+ }
+
+ public static Class> getProfilePublicKeyDataClass() {
+ return getProfilePublicKeyClass().getClasses()[0];
+ }
+
+ public static Class> getFastUtilClass(String className) {
+ return getLibraryClass("it.unimi.dsi.fastutil." + className);
+ }
+
+ public static Class> getInt2ObjectMapClass() {
+ return getFastUtilClass("ints.Int2ObjectMap");
+ }
+
+ public static Class> getIntArrayListClass() {
+ return getFastUtilClass("ints.IntArrayList");
+ }
+
+ public static Class> getLibraryClass(String classname) {
+ try {
+ return getMinecraftLibraryClass(classname);
+ } catch (RuntimeException ex) {
+ Class> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
+ setMinecraftLibraryClass(classname, clazz);
+ return clazz;
+ }
+ }
+
+ /**
+ * Fallback method that can determine the MinecraftServer and the ServerConfigurationManager.
+ */
+ private static void useFallbackServer() {
+ // Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
+ Constructor> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
+ .getConstructor(FuzzyMethodContract.newBuilder()
+ .parameterMatches(getMinecraftObjectMatcher(), 0)
+ .parameterCount(2)
+ .build());
+ Class>[] params = selected.getParameterTypes();
+
+ // Jackpot - two classes at the same time!
+ setMinecraftClass("MinecraftServer", params[0]);
+ setMinecraftClass("PlayerList", params[1]);
+ }
+
+ public static Class> getLevelChunkPacketDataClass() {
+ return getNullableNMS("network.protocol.game.ClientboundLevelChunkPacketData");
+ }
+
+ public static Class> getLightUpdatePacketDataClass() {
+ return getNullableNMS("network.protocol.game.ClientboundLightUpdatePacketData");
+ }
+
+ public static Class> getBlockEntityTypeClass() {
+ return getMinecraftClass("world.level.block.entity.BlockEntityType", "world.level.block.entity.TileEntityTypes", "TileEntityTypes");
+ }
+
+ public static Class> getBlockEntityInfoClass() {
+ try {
+ return getMinecraftClass("BlockEntityInfo");
+ } catch (RuntimeException expected) {
+ Class> infoClass = (Class>) ((ParameterizedType) FuzzyReflection.fromClass(getLevelChunkPacketDataClass(),
+ true).getFieldListByType(List.class).get(0).getGenericType()).getActualTypeArguments()[0];
+
+ setMinecraftClass("BlockEntityInfo", infoClass);
+
+ return infoClass;
+ }
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java b/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
index 398f5b68..bc7c8503 100644
--- a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
+++ b/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
@@ -1,421 +1,421 @@
-/*
- * 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.utility;
-
-import com.comphenix.protocol.ProtocolLibrary;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.Ordering;
-import java.io.Serializable;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.bukkit.Bukkit;
-import org.bukkit.Server;
-
-/**
- * Determine the current Minecraft version.
- *
- * @author Kristian
- */
-public final class MinecraftVersion implements Comparable, Serializable {
-
- /**
- * Version 1.19 - the wild update
- */
- public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19");
- /**
- * Version 1.18 - caves and cliffs part 2
- */
- public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18");
- /**
- * Version 1.17 - caves and cliffs part 1
- */
- public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
- /**
- * Version 1.16.2 - breaking change to the nether update
- */
- public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2");
- /**
- * Version 1.16.0 - the nether update
- */
- public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16");
- /**
- * Version 1.15 - the bee update
- */
- public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15");
- /**
- * Version 1.14 - village and pillage update.
- */
- public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14");
- /**
- * Version 1.13 - update aquatic.
- */
- public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
- /**
- * Version 1.12 - the world of color update.
- */
- public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
- /**
- * Version 1.11 - the exploration update.
- */
- public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
- /**
- * Version 1.10 - the frostburn update.
- */
- public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
- /**
- * Version 1.9 - the combat update.
- */
- public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9");
- /**
- * Version 1.8 - the "bountiful" update.
- */
- public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8");
- /**
- * Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise)
- */
- public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8");
- /**
- * Version 1.7.2 - the update that changed the world.
- */
- public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2");
- /**
- * Version 1.6.1 - the horse update.
- */
- public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1");
- /**
- * Version 1.5.0 - the redstone update.
- */
- public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0");
- /**
- * Version 1.4.2 - the scary update (Wither Boss).
- */
- public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2");
-
- /**
- * The latest release version of minecraft.
- */
- public static final MinecraftVersion LATEST = WILD_UPDATE;
-
- // used when serializing
- private static final long serialVersionUID = -8695133558996459770L;
-
- /**
- * Regular expression used to parse version strings.
- */
- private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*");
-
- /**
- * The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion()
- */
- private static MinecraftVersion currentVersion;
-
- private final int major;
- private final int minor;
- private final int build;
- // The development stage
- private final String development;
-
- // Snapshot?
- private final SnapshotVersion snapshot;
- private volatile Boolean atCurrentOrAbove;
-
- /**
- * Determine the current Minecraft version.
- *
- * @param server - the Bukkit server that will be used to examine the MC version.
- */
- public MinecraftVersion(Server server) {
- this(extractVersion(server.getVersion()));
- }
-
- /**
- * Construct a version object from the format major.minor.build, or the snapshot format.
- *
- * @param versionOnly - the version in text form.
- */
- public MinecraftVersion(String versionOnly) {
- this(versionOnly, true);
- }
-
- /**
- * Construct a version format from the standard release version or the snapshot verison.
- *
- * @param versionOnly - the version.
- * @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise.
- */
- private MinecraftVersion(String versionOnly, boolean parseSnapshot) {
- String[] section = versionOnly.split("-");
- SnapshotVersion snapshot = null;
- int[] numbers = new int[3];
-
- try {
- numbers = this.parseVersion(section[0]);
- } catch (NumberFormatException cause) {
- // Skip snapshot parsing
- if (!parseSnapshot) {
- throw cause;
- }
-
- try {
- // Determine if the snapshot is newer than the current release version
- snapshot = new SnapshotVersion(section[0]);
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
-
- MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false);
- boolean newer = snapshot.getSnapshotDate().compareTo(
- format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0;
-
- numbers[0] = latest.getMajor();
- numbers[1] = latest.getMinor() + (newer ? 1 : -1);
- } catch (Exception e) {
- throw new IllegalStateException("Cannot parse " + section[0], e);
- }
- }
-
- this.major = numbers[0];
- this.minor = numbers[1];
- this.build = numbers[2];
- this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null);
- this.snapshot = snapshot;
- }
-
- /**
- * Construct a version object directly.
- *
- * @param major - major version number.
- * @param minor - minor version number.
- * @param build - build version number.
- */
- public MinecraftVersion(int major, int minor, int build) {
- this(major, minor, build, null);
- }
-
- /**
- * Construct a version object directly.
- *
- * @param major - major version number.
- * @param minor - minor version number.
- * @param build - build version number.
- * @param development - development stage.
- */
- public MinecraftVersion(int major, int minor, int build, String development) {
- this.major = major;
- this.minor = minor;
- this.build = build;
- this.development = development;
- this.snapshot = null;
- }
-
- /**
- * Extract the Minecraft version from CraftBukkit itself.
- *
- * @param text - the server version in text form.
- * @return The underlying MC version.
- * @throws IllegalStateException If we could not parse the version string.
- */
- public static String extractVersion(String text) {
- Matcher version = VERSION_PATTERN.matcher(text);
-
- if (version.matches() && version.group(1) != null) {
- return version.group(1);
- } else {
- throw new IllegalStateException("Cannot parse version String '" + text + "'");
- }
- }
-
- /**
- * Parse the given server version into a Minecraft version.
- *
- * @param serverVersion - the server version.
- * @return The resulting Minecraft version.
- */
- public static MinecraftVersion fromServerVersion(String serverVersion) {
- return new MinecraftVersion(extractVersion(serverVersion));
- }
-
- public static MinecraftVersion getCurrentVersion() {
- if (currentVersion == null) {
- currentVersion = fromServerVersion(Bukkit.getVersion());
- }
-
- return currentVersion;
- }
-
- public static void setCurrentVersion(MinecraftVersion version) {
- currentVersion = version;
- }
-
- public static boolean atOrAbove(MinecraftVersion version) {
- return getCurrentVersion().isAtLeast(version);
- }
-
- private int[] parseVersion(String version) {
- String[] elements = version.split("\\.");
- int[] numbers = new int[3];
-
- // Make sure it's even a valid version
- if (elements.length < 1) {
- throw new IllegalStateException("Corrupt MC version: " + version);
- }
-
- // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
- for (int i = 0; i < Math.min(numbers.length, elements.length); i++) {
- numbers[i] = Integer.parseInt(elements[i].trim());
- }
- return numbers;
- }
-
- /**
- * Major version number
- *
- * @return Current major version number.
- */
- public int getMajor() {
- return this.major;
- }
-
- /**
- * Minor version number
- *
- * @return Current minor version number.
- */
- public int getMinor() {
- return this.minor;
- }
-
- /**
- * Build version number
- *
- * @return Current build version number.
- */
- public int getBuild() {
- return this.build;
- }
-
- /**
- * Retrieve the development stage.
- *
- * @return Development stage, or NULL if this is a release.
- */
- public String getDevelopmentStage() {
- return this.development;
- }
-
- /**
- * Retrieve the snapshot version, or NULL if this is a release.
- *
- * @return The snapshot version.
- */
- public SnapshotVersion getSnapshot() {
- return this.snapshot;
- }
-
- /**
- * Determine if this version is a snapshot.
- *
- * @return The snapshot version.
- */
- public boolean isSnapshot() {
- return this.snapshot != null;
- }
-
- /**
- * Checks if this version is at or above the current version the server is running.
- *
- * @return true if this version is equal or newer than the server version, false otherwise.
- */
- public boolean atOrAbove() {
- if (this.atCurrentOrAbove == null) {
- this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
- }
-
- return this.atCurrentOrAbove;
- }
-
- /**
- * Retrieve the version String (major.minor.build) only.
- *
- * @return A normal version string.
- */
- public String getVersion() {
- if (this.getDevelopmentStage() == null) {
- return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild());
- } else {
- return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(),
- this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : "");
- }
- }
-
- @Override
- public int compareTo(MinecraftVersion o) {
- if (o == null) {
- return 1;
- }
-
- return ComparisonChain.start()
- .compare(this.getMajor(), o.getMajor())
- .compare(this.getMinor(), o.getMinor())
- .compare(this.getBuild(), o.getBuild())
- .compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast())
- .compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst())
- .result();
- }
-
- public boolean isAtLeast(MinecraftVersion other) {
- if (other == null) {
- return false;
- }
-
- return this.compareTo(other) >= 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj == this) {
- return true;
- }
-
- if (obj instanceof MinecraftVersion) {
- MinecraftVersion other = (MinecraftVersion) obj;
-
- return this.getMajor() == other.getMajor() &&
- this.getMinor() == other.getMinor() &&
- this.getBuild() == other.getBuild() &&
- Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage());
- }
-
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild());
- }
-
- @Override
- public String toString() {
- // Convert to a String that we can parse back again
- return String.format("(MC: %s)", this.getVersion());
- }
-}
+/*
+ * 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.utility;
+
+import com.comphenix.protocol.ProtocolLibrary;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+
+/**
+ * Determine the current Minecraft version.
+ *
+ * @author Kristian
+ */
+public final class MinecraftVersion implements Comparable, Serializable {
+
+ /**
+ * Version 1.19 - the wild update
+ */
+ public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19");
+ /**
+ * Version 1.18 - caves and cliffs part 2
+ */
+ public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18");
+ /**
+ * Version 1.17 - caves and cliffs part 1
+ */
+ public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17");
+ /**
+ * Version 1.16.2 - breaking change to the nether update
+ */
+ public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2");
+ /**
+ * Version 1.16.0 - the nether update
+ */
+ public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16");
+ /**
+ * Version 1.15 - the bee update
+ */
+ public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15");
+ /**
+ * Version 1.14 - village and pillage update.
+ */
+ public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14");
+ /**
+ * Version 1.13 - update aquatic.
+ */
+ public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
+ /**
+ * Version 1.12 - the world of color update.
+ */
+ public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
+ /**
+ * Version 1.11 - the exploration update.
+ */
+ public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
+ /**
+ * Version 1.10 - the frostburn update.
+ */
+ public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
+ /**
+ * Version 1.9 - the combat update.
+ */
+ public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9");
+ /**
+ * Version 1.8 - the "bountiful" update.
+ */
+ public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8");
+ /**
+ * Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise)
+ */
+ public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8");
+ /**
+ * Version 1.7.2 - the update that changed the world.
+ */
+ public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2");
+ /**
+ * Version 1.6.1 - the horse update.
+ */
+ public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1");
+ /**
+ * Version 1.5.0 - the redstone update.
+ */
+ public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0");
+ /**
+ * Version 1.4.2 - the scary update (Wither Boss).
+ */
+ public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2");
+
+ /**
+ * The latest release version of minecraft.
+ */
+ public static final MinecraftVersion LATEST = WILD_UPDATE;
+
+ // used when serializing
+ private static final long serialVersionUID = -8695133558996459770L;
+
+ /**
+ * Regular expression used to parse version strings.
+ */
+ private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*");
+
+ /**
+ * The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion()
+ */
+ private static MinecraftVersion currentVersion;
+
+ private final int major;
+ private final int minor;
+ private final int build;
+ // The development stage
+ private final String development;
+
+ // Snapshot?
+ private final SnapshotVersion snapshot;
+ private volatile Boolean atCurrentOrAbove;
+
+ /**
+ * Determine the current Minecraft version.
+ *
+ * @param server - the Bukkit server that will be used to examine the MC version.
+ */
+ public MinecraftVersion(Server server) {
+ this(extractVersion(server.getVersion()));
+ }
+
+ /**
+ * Construct a version object from the format major.minor.build, or the snapshot format.
+ *
+ * @param versionOnly - the version in text form.
+ */
+ public MinecraftVersion(String versionOnly) {
+ this(versionOnly, true);
+ }
+
+ /**
+ * Construct a version format from the standard release version or the snapshot verison.
+ *
+ * @param versionOnly - the version.
+ * @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise.
+ */
+ private MinecraftVersion(String versionOnly, boolean parseSnapshot) {
+ String[] section = versionOnly.split("-");
+ SnapshotVersion snapshot = null;
+ int[] numbers = new int[3];
+
+ try {
+ numbers = this.parseVersion(section[0]);
+ } catch (NumberFormatException cause) {
+ // Skip snapshot parsing
+ if (!parseSnapshot) {
+ throw cause;
+ }
+
+ try {
+ // Determine if the snapshot is newer than the current release version
+ snapshot = new SnapshotVersion(section[0]);
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+
+ MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false);
+ boolean newer = snapshot.getSnapshotDate().compareTo(
+ format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0;
+
+ numbers[0] = latest.getMajor();
+ numbers[1] = latest.getMinor() + (newer ? 1 : -1);
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot parse " + section[0], e);
+ }
+ }
+
+ this.major = numbers[0];
+ this.minor = numbers[1];
+ this.build = numbers[2];
+ this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null);
+ this.snapshot = snapshot;
+ }
+
+ /**
+ * Construct a version object directly.
+ *
+ * @param major - major version number.
+ * @param minor - minor version number.
+ * @param build - build version number.
+ */
+ public MinecraftVersion(int major, int minor, int build) {
+ this(major, minor, build, null);
+ }
+
+ /**
+ * Construct a version object directly.
+ *
+ * @param major - major version number.
+ * @param minor - minor version number.
+ * @param build - build version number.
+ * @param development - development stage.
+ */
+ public MinecraftVersion(int major, int minor, int build, String development) {
+ this.major = major;
+ this.minor = minor;
+ this.build = build;
+ this.development = development;
+ this.snapshot = null;
+ }
+
+ /**
+ * Extract the Minecraft version from CraftBukkit itself.
+ *
+ * @param text - the server version in text form.
+ * @return The underlying MC version.
+ * @throws IllegalStateException If we could not parse the version string.
+ */
+ public static String extractVersion(String text) {
+ Matcher version = VERSION_PATTERN.matcher(text);
+
+ if (version.matches() && version.group(1) != null) {
+ return version.group(1);
+ } else {
+ throw new IllegalStateException("Cannot parse version String '" + text + "'");
+ }
+ }
+
+ /**
+ * Parse the given server version into a Minecraft version.
+ *
+ * @param serverVersion - the server version.
+ * @return The resulting Minecraft version.
+ */
+ public static MinecraftVersion fromServerVersion(String serverVersion) {
+ return new MinecraftVersion(extractVersion(serverVersion));
+ }
+
+ public static MinecraftVersion getCurrentVersion() {
+ if (currentVersion == null) {
+ currentVersion = fromServerVersion(Bukkit.getVersion());
+ }
+
+ return currentVersion;
+ }
+
+ public static void setCurrentVersion(MinecraftVersion version) {
+ currentVersion = version;
+ }
+
+ public static boolean atOrAbove(MinecraftVersion version) {
+ return getCurrentVersion().isAtLeast(version);
+ }
+
+ private int[] parseVersion(String version) {
+ String[] elements = version.split("\\.");
+ int[] numbers = new int[3];
+
+ // Make sure it's even a valid version
+ if (elements.length < 1) {
+ throw new IllegalStateException("Corrupt MC version: " + version);
+ }
+
+ // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively.
+ for (int i = 0; i < Math.min(numbers.length, elements.length); i++) {
+ numbers[i] = Integer.parseInt(elements[i].trim());
+ }
+ return numbers;
+ }
+
+ /**
+ * Major version number
+ *
+ * @return Current major version number.
+ */
+ public int getMajor() {
+ return this.major;
+ }
+
+ /**
+ * Minor version number
+ *
+ * @return Current minor version number.
+ */
+ public int getMinor() {
+ return this.minor;
+ }
+
+ /**
+ * Build version number
+ *
+ * @return Current build version number.
+ */
+ public int getBuild() {
+ return this.build;
+ }
+
+ /**
+ * Retrieve the development stage.
+ *
+ * @return Development stage, or NULL if this is a release.
+ */
+ public String getDevelopmentStage() {
+ return this.development;
+ }
+
+ /**
+ * Retrieve the snapshot version, or NULL if this is a release.
+ *
+ * @return The snapshot version.
+ */
+ public SnapshotVersion getSnapshot() {
+ return this.snapshot;
+ }
+
+ /**
+ * Determine if this version is a snapshot.
+ *
+ * @return The snapshot version.
+ */
+ public boolean isSnapshot() {
+ return this.snapshot != null;
+ }
+
+ /**
+ * Checks if this version is at or above the current version the server is running.
+ *
+ * @return true if this version is equal or newer than the server version, false otherwise.
+ */
+ public boolean atOrAbove() {
+ if (this.atCurrentOrAbove == null) {
+ this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
+ }
+
+ return this.atCurrentOrAbove;
+ }
+
+ /**
+ * Retrieve the version String (major.minor.build) only.
+ *
+ * @return A normal version string.
+ */
+ public String getVersion() {
+ if (this.getDevelopmentStage() == null) {
+ return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild());
+ } else {
+ return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(),
+ this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : "");
+ }
+ }
+
+ @Override
+ public int compareTo(MinecraftVersion o) {
+ if (o == null) {
+ return 1;
+ }
+
+ return ComparisonChain.start()
+ .compare(this.getMajor(), o.getMajor())
+ .compare(this.getMinor(), o.getMinor())
+ .compare(this.getBuild(), o.getBuild())
+ .compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast())
+ .compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst())
+ .result();
+ }
+
+ public boolean isAtLeast(MinecraftVersion other) {
+ if (other == null) {
+ return false;
+ }
+
+ return this.compareTo(other) >= 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof MinecraftVersion) {
+ MinecraftVersion other = (MinecraftVersion) obj;
+
+ return this.getMajor() == other.getMajor() &&
+ this.getMinor() == other.getMinor() &&
+ this.getBuild() == other.getBuild() &&
+ Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage());
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild());
+ }
+
+ @Override
+ public String toString() {
+ // Convert to a String that we can parse back again
+ return String.format("(MC: %s)", this.getVersion());
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/AutoWrapper.java b/src/main/java/com/comphenix/protocol/wrappers/AutoWrapper.java
index 5bfa0a03..647b3b19 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/AutoWrapper.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/AutoWrapper.java
@@ -1,194 +1,194 @@
-/**
- * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
- * Copyright (C) 2017 Dan Mulloy
- *
- * 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.wrappers;
-
-import com.comphenix.protocol.reflect.accessors.Accessors;
-import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
-import com.comphenix.protocol.reflect.accessors.FieldAccessor;
-import com.google.common.base.Defaults;
-
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-
-import com.comphenix.protocol.reflect.EquivalentConverter;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-/**
- * Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
- * Requirements:
- *
- * The wrapper must be public
- * If the wrapper is an internal class, it must be static
- * The wrapper must have one public constructor with no arguments (the default constructor is acceptable)
- * The wrapper must have the the same number of fields as the NMS class
- * Each field should correspond, in order, to its NMS counterpart
- * Non-generic fields must have a converter
- *
- *
- * @author dmulloy2
- */
-public class AutoWrapper implements EquivalentConverter {
- private static final Object[] NO_ARGS = new Object[0];
-
- private Map> wrappers = new HashMap<>();
- private Map> unwrappers = new HashMap<>();
-
- // lazy
- private FieldAccessor[] nmsAccessors;
- private FieldAccessor[] wrapperAccessors;
-
- private Object[] nmsDefaultArgs;
- private ConstructorAccessor nmsInstanceCreator;
-
- private Class wrapperClass;
- private Class> nmsClass;
-
- private AutoWrapper(Class wrapperClass, Class> nmsClass) {
- this.wrapperClass = wrapperClass;
- this.nmsClass = nmsClass;
- }
-
- public static AutoWrapper wrap(Class wrapperClass, Class> nmsClass) {
- return new AutoWrapper<>(wrapperClass, nmsClass);
- }
-
- public static AutoWrapper wrap(Class wrapperClass, String nmsClassName) {
- return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
- }
-
- public static AutoWrapper wrap(Class wrapperClass, String nmsClassName, String... aliases) {
- return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName, aliases));
- }
-
- public AutoWrapper field(int index, Function wrapper, Function unwrapper) {
- wrappers.put(index, wrapper);
- unwrappers.put(index, unwrapper);
- return this;
- }
-
- public AutoWrapper field(int index, EquivalentConverter converter) {
- return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
- }
-
- public T wrap(Object nmsObject) {
- T instance;
-
- try {
- instance = wrapperClass.newInstance();
- } catch (ReflectiveOperationException ex) {
- throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
- }
-
- // ensures that all accessors are present
- computeFieldAccessors();
-
- for (int i = 0; i < wrapperAccessors.length; i++) {
- FieldAccessor source = nmsAccessors[i];
- FieldAccessor target = wrapperAccessors[i];
-
- Object value = source.get(nmsObject);
- if (wrappers.containsKey(i))
- value = wrappers.get(i).apply(value);
-
- target.set(instance, value);
- }
-
- return instance;
- }
-
- public Object unwrap(Object wrapper) {
- // ensures that all accessors are present
- computeFieldAccessors();
- computeNmsConstructorAccess();
-
- Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs);
-
- for (int i = 0; i < wrapperAccessors.length; i++) {
- FieldAccessor source = wrapperAccessors[i];
- FieldAccessor target = nmsAccessors[i];
-
- Object value = source.get(wrapper);
- if (unwrappers.containsKey(i))
- value = unwrappers.get(i).apply(value);
-
- target.set(instance, value);
- }
-
- return instance;
- }
-
- private void computeFieldAccessors() {
- if (nmsAccessors == null) {
- nmsAccessors = Arrays
- .stream(nmsClass.getDeclaredFields())
- .filter(field -> !Modifier.isStatic(field.getModifiers()))
- .map(field -> Accessors.getFieldAccessor(field))
- .toArray(FieldAccessor[]::new);
- }
-
- if (wrapperAccessors == null) {
- wrapperAccessors = Arrays
- .stream(wrapperClass.getDeclaredFields())
- .map(field -> Accessors.getFieldAccessor(field))
- .toArray(FieldAccessor[]::new);
- }
- }
-
- private void computeNmsConstructorAccess() {
- if (nmsInstanceCreator == null) {
- ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass);
- if (noArgs != null) {
- // no args constructor is available - use it
- nmsInstanceCreator = noArgs;
- nmsDefaultArgs = NO_ARGS;
- } else {
- // use the first constructor of the class
- nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
- nmsDefaultArgs = Arrays
- .stream(nmsInstanceCreator.getConstructor().getParameterTypes())
- .map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
- .toArray(Object[]::new);
- }
- }
- }
-
- // ---- Equivalent conversion
-
- @Override
- public T getSpecific(Object generic) {
- return wrap(generic);
- }
-
- @Override
- public Object getGeneric(Object specific) {
- return unwrap(specific);
- }
-
- @Override
- public Class getSpecificType() {
- return wrapperClass;
- }
-
- public static class InvalidWrapperException extends RuntimeException {
- private InvalidWrapperException(String message, Throwable cause) {
- super(message, cause);
- }
- }
-}
+/**
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
+ * Copyright (C) 2017 Dan Mulloy
+ *
+ * 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.wrappers;
+
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
+import com.comphenix.protocol.reflect.accessors.FieldAccessor;
+import com.google.common.base.Defaults;
+
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+/**
+ * Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
+ * Requirements:
+ *
+ * The wrapper must be public
+ * If the wrapper is an internal class, it must be static
+ * The wrapper must have one public constructor with no arguments (the default constructor is acceptable)
+ * The wrapper must have the the same number of fields as the NMS class
+ * Each field should correspond, in order, to its NMS counterpart
+ * Non-generic fields must have a converter
+ *
+ *
+ * @author dmulloy2
+ */
+public class AutoWrapper implements EquivalentConverter {
+ private static final Object[] NO_ARGS = new Object[0];
+
+ private Map> wrappers = new HashMap<>();
+ private Map> unwrappers = new HashMap<>();
+
+ // lazy
+ private FieldAccessor[] nmsAccessors;
+ private FieldAccessor[] wrapperAccessors;
+
+ private Object[] nmsDefaultArgs;
+ private ConstructorAccessor nmsInstanceCreator;
+
+ private Class wrapperClass;
+ private Class> nmsClass;
+
+ private AutoWrapper(Class wrapperClass, Class> nmsClass) {
+ this.wrapperClass = wrapperClass;
+ this.nmsClass = nmsClass;
+ }
+
+ public static AutoWrapper wrap(Class wrapperClass, Class> nmsClass) {
+ return new AutoWrapper<>(wrapperClass, nmsClass);
+ }
+
+ public static AutoWrapper wrap(Class wrapperClass, String nmsClassName) {
+ return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
+ }
+
+ public static AutoWrapper wrap(Class wrapperClass, String nmsClassName, String... aliases) {
+ return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName, aliases));
+ }
+
+ public AutoWrapper field(int index, Function wrapper, Function unwrapper) {
+ wrappers.put(index, wrapper);
+ unwrappers.put(index, unwrapper);
+ return this;
+ }
+
+ public AutoWrapper field(int index, EquivalentConverter converter) {
+ return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
+ }
+
+ public T wrap(Object nmsObject) {
+ T instance;
+
+ try {
+ instance = wrapperClass.newInstance();
+ } catch (ReflectiveOperationException ex) {
+ throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
+ }
+
+ // ensures that all accessors are present
+ computeFieldAccessors();
+
+ for (int i = 0; i < wrapperAccessors.length; i++) {
+ FieldAccessor source = nmsAccessors[i];
+ FieldAccessor target = wrapperAccessors[i];
+
+ Object value = source.get(nmsObject);
+ if (wrappers.containsKey(i))
+ value = wrappers.get(i).apply(value);
+
+ target.set(instance, value);
+ }
+
+ return instance;
+ }
+
+ public Object unwrap(Object wrapper) {
+ // ensures that all accessors are present
+ computeFieldAccessors();
+ computeNmsConstructorAccess();
+
+ Object instance = nmsInstanceCreator.invoke(nmsDefaultArgs);
+
+ for (int i = 0; i < wrapperAccessors.length; i++) {
+ FieldAccessor source = wrapperAccessors[i];
+ FieldAccessor target = nmsAccessors[i];
+
+ Object value = source.get(wrapper);
+ if (unwrappers.containsKey(i))
+ value = unwrappers.get(i).apply(value);
+
+ target.set(instance, value);
+ }
+
+ return instance;
+ }
+
+ private void computeFieldAccessors() {
+ if (nmsAccessors == null) {
+ nmsAccessors = Arrays
+ .stream(nmsClass.getDeclaredFields())
+ .filter(field -> !Modifier.isStatic(field.getModifiers()))
+ .map(field -> Accessors.getFieldAccessor(field))
+ .toArray(FieldAccessor[]::new);
+ }
+
+ if (wrapperAccessors == null) {
+ wrapperAccessors = Arrays
+ .stream(wrapperClass.getDeclaredFields())
+ .map(field -> Accessors.getFieldAccessor(field))
+ .toArray(FieldAccessor[]::new);
+ }
+ }
+
+ private void computeNmsConstructorAccess() {
+ if (nmsInstanceCreator == null) {
+ ConstructorAccessor noArgs = Accessors.getConstructorAccessorOrNull(nmsClass);
+ if (noArgs != null) {
+ // no args constructor is available - use it
+ nmsInstanceCreator = noArgs;
+ nmsDefaultArgs = NO_ARGS;
+ } else {
+ // use the first constructor of the class
+ nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
+ nmsDefaultArgs = Arrays
+ .stream(nmsInstanceCreator.getConstructor().getParameterTypes())
+ .map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
+ .toArray(Object[]::new);
+ }
+ }
+ }
+
+ // ---- Equivalent conversion
+
+ @Override
+ public T getSpecific(Object generic) {
+ return wrap(generic);
+ }
+
+ @Override
+ public Object getGeneric(Object specific) {
+ return unwrap(specific);
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return wrapperClass;
+ }
+
+ public static class InvalidWrapperException extends RuntimeException {
+ private InvalidWrapperException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/ClonableWrapper.java b/src/main/java/com/comphenix/protocol/wrappers/ClonableWrapper.java
index ec671081..5a80b39e 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/ClonableWrapper.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/ClonableWrapper.java
@@ -1,7 +1,7 @@
-package com.comphenix.protocol.wrappers;
-
-public interface ClonableWrapper {
- Object getHandle();
- ClonableWrapper deepClone();
-
-}
+package com.comphenix.protocol.wrappers;
+
+public interface ClonableWrapper {
+ Object getHandle();
+ ClonableWrapper deepClone();
+
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/ComponentParser.java b/src/main/java/com/comphenix/protocol/wrappers/ComponentParser.java
index 97a57923..8f7ec892 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/ComponentParser.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/ComponentParser.java
@@ -1,75 +1,75 @@
-/**
- * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
- * Copyright (C) 2016 dmulloy2
- *
- * 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.wrappers;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
-
-/**
- * Handles component parsing in 1.8
- * @author dmulloy2
- */
-public class ComponentParser {
-
- private static Constructor readerConstructor;
- private static Method setLenient;
- private static Method getAdapter;
- private static Method read;
-
- private ComponentParser() {
- }
-
- public static Object deserialize(Object gson, Class> component, StringReader str) {
- try {
- com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
- reader.setLenient(true);
- return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
- } catch (IOException ex) {
- throw new RuntimeException("Failed to read JSON", ex);
- } catch (LinkageError er) {
- return deserializeLegacy(gson, component, str);
- }
- }
-
- // Should only be needed on 1.8.
- private static Object deserializeLegacy(Object gson, Class> component, StringReader str) {
- try {
- if (readerConstructor == null) {
-
- Class> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
- readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
- readerConstructor.setAccessible(true);
- setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
- setLenient.setAccessible(true);
- getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
- getAdapter.setAccessible(true);
- Object adapter = getAdapter.invoke(gson, component);
- read = adapter.getClass().getDeclaredMethod("read", readerClass);
- read.setAccessible(true);
- }
- Object reader = readerConstructor.newInstance(str);
- setLenient.invoke(reader, true);
- Object adapter = getAdapter.invoke(gson, component);
- return read.invoke(adapter, reader);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException("Failed to read JSON", ex);
- }
- }
-}
+/**
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
+ * Copyright (C) 2016 dmulloy2
+ *
+ * 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.wrappers;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+/**
+ * Handles component parsing in 1.8
+ * @author dmulloy2
+ */
+public class ComponentParser {
+
+ private static Constructor readerConstructor;
+ private static Method setLenient;
+ private static Method getAdapter;
+ private static Method read;
+
+ private ComponentParser() {
+ }
+
+ public static Object deserialize(Object gson, Class> component, StringReader str) {
+ try {
+ com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
+ reader.setLenient(true);
+ return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
+ } catch (IOException ex) {
+ throw new RuntimeException("Failed to read JSON", ex);
+ } catch (LinkageError er) {
+ return deserializeLegacy(gson, component, str);
+ }
+ }
+
+ // Should only be needed on 1.8.
+ private static Object deserializeLegacy(Object gson, Class> component, StringReader str) {
+ try {
+ if (readerConstructor == null) {
+
+ Class> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
+ readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
+ readerConstructor.setAccessible(true);
+ setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
+ setLenient.setAccessible(true);
+ getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
+ getAdapter.setAccessible(true);
+ Object adapter = getAdapter.invoke(gson, component);
+ read = adapter.getClass().getDeclaredMethod("read", readerClass);
+ read.setAccessible(true);
+ }
+ Object reader = readerConstructor.newInstance(str);
+ setLenient.invoke(reader, true);
+ Object adapter = getAdapter.invoke(gson, component);
+ return read.invoke(adapter, reader);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException("Failed to read JSON", ex);
+ }
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/Converters.java b/src/main/java/com/comphenix/protocol/wrappers/Converters.java
index 870a2226..e4ad486e 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/Converters.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/Converters.java
@@ -1,174 +1,174 @@
-/**
- * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
- * Copyright (C) 2017 Dan Mulloy
- *
- * 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.wrappers;
-
-import java.lang.reflect.Array;
-import java.util.Optional;
-import java.util.function.Function;
-
-import com.comphenix.protocol.reflect.EquivalentConverter;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-/**
- * Utility class for converters
- * @author dmulloy2
- */
-@SuppressWarnings("unchecked")
-public class Converters {
-
- /**
- * Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
- * @param converter Underlying converter
- * @param Element type
- * @return An ignore null converter
- */
- public static EquivalentConverter ignoreNull(final EquivalentConverter converter) {
- return new EquivalentConverter() {
- @Override
- public T getSpecific(Object generic) {
- return generic != null ? converter.getSpecific(generic) : null;
- }
-
- @Override
- public Object getGeneric(T specific) {
- return specific != null ? converter.getGeneric(specific) : null;
- }
-
- @Override
- public Class getSpecificType() {
- return converter.getSpecificType();
- }
- };
- }
-
- /**
- * Returns a converter that passes generic and specific values through without converting.
- * @param clazz Element class
- * @param Element type
- * @return A passthrough converter
- */
- public static EquivalentConverter passthrough(final Class clazz) {
- return ignoreNull(new EquivalentConverter() {
- @Override
- public T getSpecific(Object generic) {
- return (T) generic;
- }
-
- @Override
- public Object getGeneric(T specific) {
- return specific;
- }
-
- @Override
- public Class getSpecificType() {
- return clazz;
- }
- });
- }
-
- /**
- * Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
- * converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
- * @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
- * @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
- * @param Wrapper type
- * @return A handle converter
- */
- public static EquivalentConverter handle(final Function toHandle,
- final Function fromHandle, final Class specificType) {
- return new EquivalentConverter() {
- @Override
- public T getSpecific(Object generic) {
- return fromHandle.apply(generic);
- }
-
- @Override
- public Object getGeneric(T specific) {
- return toHandle.apply(specific);
- }
-
- @Override
- public Class getSpecificType() {
- return specificType;
- }
- };
- }
-
- /**
- * Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting
- * each element individually.
- *
- * @param nmsClass NMS class
- * @param converter Underlying converter
- * @param Generic type
- * @return An array converter
- */
- public static EquivalentConverter array(final Class> nmsClass, final EquivalentConverter converter) {
- return new EquivalentConverter() {
- @Override
- public T[] getSpecific(Object generic) {
- Object[] array = (Object[]) generic;
- Class clazz = getSpecificType();
- T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length));
-
- // Unwrap every item
- for (int i = 0; i < result.length; i++) {
- result[i] = converter.getSpecific(array[i]);
- }
-
- return result;
- }
-
- @Override
- public Object getGeneric(T[] specific) {
- Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
-
- // Wrap every item
- for (int i = 0; i < result.length; i++) {
- result[i] = converter.getGeneric(specific[i]);
- }
-
- return result;
- }
-
- @Override
- public Class getSpecificType() {
- return (Class) MinecraftReflection.getArrayClass(converter.getSpecificType());
- }
- };
- }
-
- public static EquivalentConverter> optional(final EquivalentConverter converter) {
- return new EquivalentConverter>() {
- @Override
- public Object getGeneric(Optional specific) {
- return specific.map(converter::getGeneric);
- }
-
- @Override
- public Optional getSpecific(Object generic) {
- Optional optional = (Optional) generic;
- return optional.map(converter::getSpecific);
- }
-
- @Override
- public Class> getSpecificType() {
- return (Class>) Optional.empty().getClass();
- }
- };
- }
-}
+/**
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
+ * Copyright (C) 2017 Dan Mulloy
+ *
+ * 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.wrappers;
+
+import java.lang.reflect.Array;
+import java.util.Optional;
+import java.util.function.Function;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+/**
+ * Utility class for converters
+ * @author dmulloy2
+ */
+@SuppressWarnings("unchecked")
+public class Converters {
+
+ /**
+ * Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
+ * @param converter Underlying converter
+ * @param Element type
+ * @return An ignore null converter
+ */
+ public static EquivalentConverter ignoreNull(final EquivalentConverter converter) {
+ return new EquivalentConverter() {
+ @Override
+ public T getSpecific(Object generic) {
+ return generic != null ? converter.getSpecific(generic) : null;
+ }
+
+ @Override
+ public Object getGeneric(T specific) {
+ return specific != null ? converter.getGeneric(specific) : null;
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return converter.getSpecificType();
+ }
+ };
+ }
+
+ /**
+ * Returns a converter that passes generic and specific values through without converting.
+ * @param clazz Element class
+ * @param Element type
+ * @return A passthrough converter
+ */
+ public static EquivalentConverter passthrough(final Class clazz) {
+ return ignoreNull(new EquivalentConverter() {
+ @Override
+ public T getSpecific(Object generic) {
+ return (T) generic;
+ }
+
+ @Override
+ public Object getGeneric(T specific) {
+ return specific;
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return clazz;
+ }
+ });
+ }
+
+ /**
+ * Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
+ * converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
+ * @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
+ * @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
+ * @param Wrapper type
+ * @return A handle converter
+ */
+ public static EquivalentConverter handle(final Function toHandle,
+ final Function fromHandle, final Class specificType) {
+ return new EquivalentConverter() {
+ @Override
+ public T getSpecific(Object generic) {
+ return fromHandle.apply(generic);
+ }
+
+ @Override
+ public Object getGeneric(T specific) {
+ return toHandle.apply(specific);
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return specificType;
+ }
+ };
+ }
+
+ /**
+ * Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting
+ * each element individually.
+ *
+ * @param nmsClass NMS class
+ * @param converter Underlying converter
+ * @param Generic type
+ * @return An array converter
+ */
+ public static EquivalentConverter array(final Class> nmsClass, final EquivalentConverter converter) {
+ return new EquivalentConverter() {
+ @Override
+ public T[] getSpecific(Object generic) {
+ Object[] array = (Object[]) generic;
+ Class clazz = getSpecificType();
+ T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length));
+
+ // Unwrap every item
+ for (int i = 0; i < result.length; i++) {
+ result[i] = converter.getSpecific(array[i]);
+ }
+
+ return result;
+ }
+
+ @Override
+ public Object getGeneric(T[] specific) {
+ Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
+
+ // Wrap every item
+ for (int i = 0; i < result.length; i++) {
+ result[i] = converter.getGeneric(specific[i]);
+ }
+
+ return result;
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return (Class) MinecraftReflection.getArrayClass(converter.getSpecificType());
+ }
+ };
+ }
+
+ public static EquivalentConverter> optional(final EquivalentConverter converter) {
+ return new EquivalentConverter>() {
+ @Override
+ public Object getGeneric(Optional specific) {
+ return specific.map(converter::getGeneric);
+ }
+
+ @Override
+ public Optional getSpecific(Object generic) {
+ Optional optional = (Optional) generic;
+ return optional.map(converter::getSpecific);
+ }
+
+ @Override
+ public Class> getSpecificType() {
+ return (Class>) Optional.empty().getClass();
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/MovingObjectPositionBlock.java b/src/main/java/com/comphenix/protocol/wrappers/MovingObjectPositionBlock.java
index 9ff4f0cd..79af6b5d 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/MovingObjectPositionBlock.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/MovingObjectPositionBlock.java
@@ -1,103 +1,103 @@
-package com.comphenix.protocol.wrappers;
-
-import com.comphenix.protocol.reflect.EquivalentConverter;
-import com.comphenix.protocol.reflect.StructureModifier;
-import com.comphenix.protocol.reflect.accessors.Accessors;
-import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
-import com.comphenix.protocol.utility.MinecraftReflection;
-import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
-
-import org.bukkit.util.Vector;
-
-public class MovingObjectPositionBlock implements Cloneable {
- private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS(
- "world.phys.MovingObjectPositionBlock", "world.phys.BlockHitResult", "MovingObjectPositionBlock");
-
- private BlockPosition position;
- private Vector posVector;
- private Direction direction;
- private boolean insideBlock;
-
- public MovingObjectPositionBlock() { }
-
- public MovingObjectPositionBlock(BlockPosition position, Vector posVector, Direction direction, boolean insideBlock) {
- this.position = position;
- this.posVector = posVector;
- this.direction = direction;
- this.insideBlock = insideBlock;
- }
-
- public static Class> getNmsClass() {
- return NMS_CLASS;
- }
-
- public BlockPosition getBlockPosition() {
- return position;
- }
-
- public void setBlockPosition(BlockPosition position) {
- this.position = position;
- }
-
- public Vector getPosVector() {
- return posVector;
- }
-
- public void setPosVector(Vector vector) {
- this.posVector = vector;
- }
-
- public Direction getDirection() {
- return direction;
- }
-
- public void setDirection(Direction direction) {
- this.direction = direction;
- }
-
- public boolean isInsideBlock() {
- return insideBlock;
- }
-
- public void setInsideBlock(boolean insideBlock) {
- this.insideBlock = insideBlock;
- }
-
- private static ConstructorAccessor constructor;
-
- public static EquivalentConverter getConverter() {
- return Converters.ignoreNull(new EquivalentConverter() {
- @Override
- public Object getGeneric(MovingObjectPositionBlock specific) {
- if (constructor == null) {
- constructor = Accessors.getConstructorAccessor(NMS_CLASS,
- MinecraftReflection.getVec3DClass(),
- EnumWrappers.getDirectionClass(),
- MinecraftReflection.getBlockPositionClass(),
- boolean.class);
- }
-
- Object nmsVector = BukkitConverters.getVectorConverter().getGeneric(specific.posVector);
- Object nmsDirection = EnumWrappers.getDirectionConverter().getGeneric(specific.direction);
- Object nmsBlockPos = BlockPosition.getConverter().getGeneric(specific.position);
-
- return constructor.invoke(nmsVector, nmsDirection, nmsBlockPos, specific.insideBlock);
- }
-
- @Override
- public MovingObjectPositionBlock getSpecific(Object generic) {
- StructureModifier modifier = new StructureModifier<>(generic.getClass()).withTarget(generic);
- Direction direction = modifier.withType(EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()).read(0);
- BlockPosition blockPos = modifier.withType(MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()).read(0);
- Vector posVector = modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
- boolean insideBlock = (boolean) modifier.withType(boolean.class).read(1);
- return new MovingObjectPositionBlock(blockPos, posVector, direction, insideBlock);
- }
-
- @Override
- public Class getSpecificType() {
- return MovingObjectPositionBlock.class;
- }
- });
- }
-}
+package com.comphenix.protocol.wrappers;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
+
+import org.bukkit.util.Vector;
+
+public class MovingObjectPositionBlock implements Cloneable {
+ private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS(
+ "world.phys.MovingObjectPositionBlock", "world.phys.BlockHitResult", "MovingObjectPositionBlock");
+
+ private BlockPosition position;
+ private Vector posVector;
+ private Direction direction;
+ private boolean insideBlock;
+
+ public MovingObjectPositionBlock() { }
+
+ public MovingObjectPositionBlock(BlockPosition position, Vector posVector, Direction direction, boolean insideBlock) {
+ this.position = position;
+ this.posVector = posVector;
+ this.direction = direction;
+ this.insideBlock = insideBlock;
+ }
+
+ public static Class> getNmsClass() {
+ return NMS_CLASS;
+ }
+
+ public BlockPosition getBlockPosition() {
+ return position;
+ }
+
+ public void setBlockPosition(BlockPosition position) {
+ this.position = position;
+ }
+
+ public Vector getPosVector() {
+ return posVector;
+ }
+
+ public void setPosVector(Vector vector) {
+ this.posVector = vector;
+ }
+
+ public Direction getDirection() {
+ return direction;
+ }
+
+ public void setDirection(Direction direction) {
+ this.direction = direction;
+ }
+
+ public boolean isInsideBlock() {
+ return insideBlock;
+ }
+
+ public void setInsideBlock(boolean insideBlock) {
+ this.insideBlock = insideBlock;
+ }
+
+ private static ConstructorAccessor constructor;
+
+ public static EquivalentConverter getConverter() {
+ return Converters.ignoreNull(new EquivalentConverter() {
+ @Override
+ public Object getGeneric(MovingObjectPositionBlock specific) {
+ if (constructor == null) {
+ constructor = Accessors.getConstructorAccessor(NMS_CLASS,
+ MinecraftReflection.getVec3DClass(),
+ EnumWrappers.getDirectionClass(),
+ MinecraftReflection.getBlockPositionClass(),
+ boolean.class);
+ }
+
+ Object nmsVector = BukkitConverters.getVectorConverter().getGeneric(specific.posVector);
+ Object nmsDirection = EnumWrappers.getDirectionConverter().getGeneric(specific.direction);
+ Object nmsBlockPos = BlockPosition.getConverter().getGeneric(specific.position);
+
+ return constructor.invoke(nmsVector, nmsDirection, nmsBlockPos, specific.insideBlock);
+ }
+
+ @Override
+ public MovingObjectPositionBlock getSpecific(Object generic) {
+ StructureModifier modifier = new StructureModifier<>(generic.getClass()).withTarget(generic);
+ Direction direction = modifier.withType(EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()).read(0);
+ BlockPosition blockPos = modifier.withType(MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()).read(0);
+ Vector posVector = modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
+ boolean insideBlock = (boolean) modifier.withType(boolean.class).read(1);
+ return new MovingObjectPositionBlock(blockPos, posVector, direction, insideBlock);
+ }
+
+ @Override
+ public Class getSpecificType() {
+ return MovingObjectPositionBlock.class;
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/Pair.java b/src/main/java/com/comphenix/protocol/wrappers/Pair.java
index 6a466fca..060a00eb 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/Pair.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/Pair.java
@@ -1,43 +1,43 @@
-package com.comphenix.protocol.wrappers;
-
-import java.util.Objects;
-
-public class Pair {
- private A first;
- private B second;
-
- public Pair(A first, B second) {
- this.first = first;
- this.second = second;
- }
-
- public A getFirst() {
- return first;
- }
-
- public B getSecond() {
- return second;
- }
-
- public void setFirst(A first) {
- this.first = first;
- }
-
- public void setSecond(B second) {
- this.second = second;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Pair, ?> pair = (Pair, ?>) o;
- return Objects.equals(first, pair.first) &&
- Objects.equals(second, pair.second);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(first, second);
- }
-}
+package com.comphenix.protocol.wrappers;
+
+import java.util.Objects;
+
+public class Pair {
+ private A first;
+ private B second;
+
+ public Pair(A first, B second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public A getFirst() {
+ return first;
+ }
+
+ public B getSecond() {
+ return second;
+ }
+
+ public void setFirst(A first) {
+ this.first = first;
+ }
+
+ public void setSecond(B second) {
+ this.second = second;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Pair, ?> pair = (Pair, ?>) o;
+ return Objects.equals(first, pair.first) &&
+ Objects.equals(second, pair.second);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(first, second);
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/Vector3F.java b/src/main/java/com/comphenix/protocol/wrappers/Vector3F.java
index d02b9b5c..6402956e 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/Vector3F.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/Vector3F.java
@@ -1,127 +1,127 @@
-/**
- * (c) 2016 dmulloy2
- */
-package com.comphenix.protocol.wrappers;
-
-import com.comphenix.protocol.reflect.EquivalentConverter;
-import com.comphenix.protocol.reflect.StructureModifier;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-import java.lang.reflect.Constructor;
-
-/**
- * @author dmulloy2
- */
-public class Vector3F {
- protected float x;
- protected float y;
- protected float z;
-
- public Vector3F() {
- this(0, 0, 0);
- }
-
- public Vector3F(float x, float y, float z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public float getX() {
- return x;
- }
-
- public Vector3F setX(float x) {
- this.x = x;
- return this;
- }
-
- public float getY() {
- return y;
- }
-
- public Vector3F setY(float y) {
- this.y = y;
- return this;
- }
-
- public float getZ() {
- return z;
- }
-
- public Vector3F setZ(float z) {
- this.z = z;
- return this;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Float.floatToIntBits(x);
- result = prime * result + Float.floatToIntBits(y);
- result = prime * result + Float.floatToIntBits(z);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
-
- if (obj instanceof Vector3F) {
- Vector3F that = (Vector3F) obj;
- if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
- return false;
- if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
- return false;
- if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
- return false;
- return true;
- }
-
- return false;
- }
-
- private static Constructor> constructor = null;
- private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS("core.Vector3f", "core.Rotations", "Vector3f");
-
- public static Class> getMinecraftClass() {
- return NMS_CLASS;
- }
-
- public static EquivalentConverter getConverter() {
- return Converters.ignoreNull(new EquivalentConverter() {
- @Override
- public Class getSpecificType() {
- return Vector3F.class;
- }
-
- @Override
- public Object getGeneric(Vector3F specific) {
- if (constructor == null) {
- try {
- constructor = NMS_CLASS.getConstructor(float.class, float.class, float.class);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException("Failed to find constructor for Vector3f", ex);
- }
- }
-
- try {
- return constructor.newInstance(specific.x, specific.y, specific.z);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException("Failed to create new instance of Vector3f", ex);
- }
- }
-
- @Override
- public Vector3F getSpecific(Object generic) {
- StructureModifier modifier = new StructureModifier(generic.getClass())
- .withTarget(generic).withType(float.class);
- float x = modifier.read(0);
- float y = modifier.read(1);
- float z = modifier.read(2);
- return new Vector3F(x, y, z);
- }
- });
- }
+/**
+ * (c) 2016 dmulloy2
+ */
+package com.comphenix.protocol.wrappers;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author dmulloy2
+ */
+public class Vector3F {
+ protected float x;
+ protected float y;
+ protected float z;
+
+ public Vector3F() {
+ this(0, 0, 0);
+ }
+
+ public Vector3F(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public Vector3F setX(float x) {
+ this.x = x;
+ return this;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public Vector3F setY(float y) {
+ this.y = y;
+ return this;
+ }
+
+ public float getZ() {
+ return z;
+ }
+
+ public Vector3F setZ(float z) {
+ this.z = z;
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(x);
+ result = prime * result + Float.floatToIntBits(y);
+ result = prime * result + Float.floatToIntBits(z);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (obj instanceof Vector3F) {
+ Vector3F that = (Vector3F) obj;
+ if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
+ return false;
+ if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
+ return false;
+ if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
+ return false;
+ return true;
+ }
+
+ return false;
+ }
+
+ private static Constructor> constructor = null;
+ private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS("core.Vector3f", "core.Rotations", "Vector3f");
+
+ public static Class> getMinecraftClass() {
+ return NMS_CLASS;
+ }
+
+ public static EquivalentConverter getConverter() {
+ return Converters.ignoreNull(new EquivalentConverter() {
+ @Override
+ public Class getSpecificType() {
+ return Vector3F.class;
+ }
+
+ @Override
+ public Object getGeneric(Vector3F specific) {
+ if (constructor == null) {
+ try {
+ constructor = NMS_CLASS.getConstructor(float.class, float.class, float.class);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException("Failed to find constructor for Vector3f", ex);
+ }
+ }
+
+ try {
+ return constructor.newInstance(specific.x, specific.y, specific.z);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException("Failed to create new instance of Vector3f", ex);
+ }
+ }
+
+ @Override
+ public Vector3F getSpecific(Object generic) {
+ StructureModifier modifier = new StructureModifier(generic.getClass())
+ .withTarget(generic).withType(float.class);
+ float x = modifier.read(0);
+ float y = modifier.read(1);
+ float z = modifier.read(2);
+ return new Vector3F(x, y, z);
+ }
+ });
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java
index 3a663778..04f9e19c 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java
@@ -1,169 +1,169 @@
-package com.comphenix.protocol.wrappers;
-
-import java.lang.reflect.Modifier;
-
-import com.comphenix.protocol.reflect.FuzzyReflection;
-import com.comphenix.protocol.reflect.StructureModifier;
-import com.comphenix.protocol.reflect.accessors.Accessors;
-import com.comphenix.protocol.reflect.accessors.MethodAccessor;
-import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-import com.comphenix.protocol.utility.MinecraftVersion;
-import com.mojang.math.Vector3fa;
-import org.bukkit.Color;
-import org.bukkit.Particle;
-import org.bukkit.inventory.ItemStack;
-
-/**
- * Represents an immutable wrapped ParticleParam in 1.13
- */
-public class WrappedParticle {
- private static MethodAccessor toBukkit;
- private static MethodAccessor toNMS;
- private static MethodAccessor toCraftData;
-
- private static void ensureMethods() {
- if (toBukkit != null && toNMS != null) {
- return;
- }
-
- FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
- FuzzyMethodContract contract = FuzzyMethodContract
- .newBuilder()
- .requireModifier(Modifier.STATIC)
- .returnTypeExact(Particle.class)
- .parameterExactType(MinecraftReflection.getParticleParam())
- .build();
- toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
-
- contract = FuzzyMethodContract
- .newBuilder()
- .requireModifier(Modifier.STATIC)
- .returnTypeExact(MinecraftReflection.getParticleParam())
- .parameterCount(2)
- .build();
- toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
-
- Class> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
- fuzzy = FuzzyReflection.fromClass(cbData);
- contract = FuzzyMethodContract
- .newBuilder()
- .requireModifier(Modifier.STATIC)
- .returnTypeExact(cbData)
- .parameterExactArray(MinecraftReflection.getIBlockDataClass())
- .build();
- toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
- }
-
- private final Particle particle;
- private final T data;
- private final Object handle;
-
- private WrappedParticle(Object handle, Particle particle, T data) {
- this.handle = handle;
- this.particle = particle;
- this.data = data;
- }
-
- /**
- * @return This particle's Bukkit type
- */
- public Particle getParticle() {
- return particle;
- }
-
- /**
- * Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
- * {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
- * for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
- * be {@link Particle.DustOptions}
- *
- * @return The particle data
- */
- public T getData() {
- return data;
- }
-
- /**
- * @return NMS handle
- */
- public Object getHandle() {
- return handle;
- }
-
- public static WrappedParticle fromHandle(Object handle) {
- ensureMethods();
-
- Particle bukkit = (Particle) toBukkit.invoke(null, handle);
- Object data = null;
-
- switch (bukkit) {
- case BLOCK_CRACK:
- case BLOCK_DUST:
- case FALLING_DUST:
- data = getBlockData(handle);
- break;
- case ITEM_CRACK:
- data = getItem(handle);
- break;
- case REDSTONE:
- data = getRedstone(handle);
- break;
- default:
- break;
- }
-
- return new WrappedParticle<>(handle, bukkit, data);
- }
-
- private static WrappedBlockData getBlockData(Object handle) {
- return new StructureModifier<>(handle.getClass())
- .withTarget(handle)
- .withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
- .read(0);
- }
-
- private static Object getItem(Object handle) {
- return new StructureModifier<>(handle.getClass())
- .withTarget(handle)
- .withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
- .read(0);
- }
-
- private static Object getRedstone(Object handle) {
- int r, g, b;
- float alpha;
-
- if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
- StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
- Vector3fa rgb = (Vector3fa) modifier.withType(Vector3fa.class).read(0);
-
- r = (int) (rgb.a() * 255);
- g = (int) (rgb.b() * 255);
- b = (int) (rgb.c() * 255);
- alpha = (float) modifier.withType(float.class).read(0);
- } else {
- StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
- r = (int) (modifier.read(0) * 255);
- g = (int) (modifier.read(1) * 255);
- b = (int) (modifier.read(2) * 255);
- alpha = modifier.read(3);
- }
-
- return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha);
- }
-
- public static WrappedParticle create(Particle particle, T data) {
- ensureMethods();
-
- Object bukkitData = data;
- if (data instanceof WrappedBlockData) {
- WrappedBlockData blockData = (WrappedBlockData) data;
- bukkitData = toCraftData.invoke(null, blockData.getHandle());
- }
-
- Object handle = toNMS.invoke(null, particle, bukkitData);
- return new WrappedParticle<>(handle, particle, data);
- }
-}
+package com.comphenix.protocol.wrappers;
+
+import java.lang.reflect.Modifier;
+
+import com.comphenix.protocol.reflect.FuzzyReflection;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.MethodAccessor;
+import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+import com.comphenix.protocol.utility.MinecraftVersion;
+import com.mojang.math.Vector3fa;
+import org.bukkit.Color;
+import org.bukkit.Particle;
+import org.bukkit.inventory.ItemStack;
+
+/**
+ * Represents an immutable wrapped ParticleParam in 1.13
+ */
+public class WrappedParticle {
+ private static MethodAccessor toBukkit;
+ private static MethodAccessor toNMS;
+ private static MethodAccessor toCraftData;
+
+ private static void ensureMethods() {
+ if (toBukkit != null && toNMS != null) {
+ return;
+ }
+
+ FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
+ FuzzyMethodContract contract = FuzzyMethodContract
+ .newBuilder()
+ .requireModifier(Modifier.STATIC)
+ .returnTypeExact(Particle.class)
+ .parameterExactType(MinecraftReflection.getParticleParam())
+ .build();
+ toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
+
+ contract = FuzzyMethodContract
+ .newBuilder()
+ .requireModifier(Modifier.STATIC)
+ .returnTypeExact(MinecraftReflection.getParticleParam())
+ .parameterCount(2)
+ .build();
+ toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
+
+ Class> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
+ fuzzy = FuzzyReflection.fromClass(cbData);
+ contract = FuzzyMethodContract
+ .newBuilder()
+ .requireModifier(Modifier.STATIC)
+ .returnTypeExact(cbData)
+ .parameterExactArray(MinecraftReflection.getIBlockDataClass())
+ .build();
+ toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
+ }
+
+ private final Particle particle;
+ private final T data;
+ private final Object handle;
+
+ private WrappedParticle(Object handle, Particle particle, T data) {
+ this.handle = handle;
+ this.particle = particle;
+ this.data = data;
+ }
+
+ /**
+ * @return This particle's Bukkit type
+ */
+ public Particle getParticle() {
+ return particle;
+ }
+
+ /**
+ * Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
+ * {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
+ * for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
+ * be {@link Particle.DustOptions}
+ *
+ * @return The particle data
+ */
+ public T getData() {
+ return data;
+ }
+
+ /**
+ * @return NMS handle
+ */
+ public Object getHandle() {
+ return handle;
+ }
+
+ public static WrappedParticle fromHandle(Object handle) {
+ ensureMethods();
+
+ Particle bukkit = (Particle) toBukkit.invoke(null, handle);
+ Object data = null;
+
+ switch (bukkit) {
+ case BLOCK_CRACK:
+ case BLOCK_DUST:
+ case FALLING_DUST:
+ data = getBlockData(handle);
+ break;
+ case ITEM_CRACK:
+ data = getItem(handle);
+ break;
+ case REDSTONE:
+ data = getRedstone(handle);
+ break;
+ default:
+ break;
+ }
+
+ return new WrappedParticle<>(handle, bukkit, data);
+ }
+
+ private static WrappedBlockData getBlockData(Object handle) {
+ return new StructureModifier<>(handle.getClass())
+ .withTarget(handle)
+ .withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
+ .read(0);
+ }
+
+ private static Object getItem(Object handle) {
+ return new StructureModifier<>(handle.getClass())
+ .withTarget(handle)
+ .withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
+ .read(0);
+ }
+
+ private static Object getRedstone(Object handle) {
+ int r, g, b;
+ float alpha;
+
+ if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
+ StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
+ Vector3fa rgb = (Vector3fa) modifier.withType(Vector3fa.class).read(0);
+
+ r = (int) (rgb.a() * 255);
+ g = (int) (rgb.b() * 255);
+ b = (int) (rgb.c() * 255);
+ alpha = (float) modifier.withType(float.class).read(0);
+ } else {
+ StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
+ r = (int) (modifier.read(0) * 255);
+ g = (int) (modifier.read(1) * 255);
+ b = (int) (modifier.read(2) * 255);
+ alpha = modifier.read(3);
+ }
+
+ return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha);
+ }
+
+ public static WrappedParticle create(Particle particle, T data) {
+ ensureMethods();
+
+ Object bukkitData = data;
+ if (data instanceof WrappedBlockData) {
+ WrappedBlockData blockData = (WrappedBlockData) data;
+ bukkitData = toCraftData.invoke(null, blockData.getHandle());
+ }
+
+ Object handle = toNMS.invoke(null, particle, bukkitData);
+ return new WrappedParticle<>(handle, particle, data);
+ }
+}
diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedVillagerData.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedVillagerData.java
index e41ed1f4..bb7579d7 100644
--- a/src/main/java/com/comphenix/protocol/wrappers/WrappedVillagerData.java
+++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedVillagerData.java
@@ -1,84 +1,84 @@
-package com.comphenix.protocol.wrappers;
-
-import com.comphenix.protocol.reflect.EquivalentConverter;
-import com.comphenix.protocol.reflect.StructureModifier;
-import com.comphenix.protocol.reflect.accessors.Accessors;
-import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
-import com.comphenix.protocol.utility.MinecraftReflection;
-
-public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
- private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS(
- "world.entity.npc.VillagerData","VillagerData");
- private static final Class> TYPE_CLASS = MinecraftReflection.getNullableNMS(
- "world.entity.npc.VillagerType", "VillagerType");
- private static final Class> PROF_CLASS = MinecraftReflection.getNullableNMS(
- "world.entity.npc.VillagerProfession", "VillagerProfession");
-
- private static EquivalentConverter TYPE_CONVERTER;
- private static EquivalentConverter PROF_CONVERTER;
-
- static {
- if (NMS_CLASS != null) {
- TYPE_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Type.class, TYPE_CLASS);
- PROF_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Profession.class, PROF_CLASS);
- }
- }
-
- public enum Type {
- DESERT, JUNGLE, PLAINS, SAVANNA, SNOW, SWAMP, TAIGA
- }
-
- public enum Profession {
- NONE, ARMORER, BUTCHER, CARTOGRAPHER, CLERIC, FARMER, FISHERMAN,
- FLETCHER, LEATHERWORKER, LIBRARIAN, MASON, NITWIT, SHEPHERD,
- TOOLSMITH, WEAPONSMITH
- }
-
- private StructureModifier modifier;
-
- private WrappedVillagerData(Object handle) {
- super(NMS_CLASS);
- setHandle(handle);
-
- modifier = new StructureModifier<>(NMS_CLASS).withTarget(handle);
- }
-
- public static WrappedVillagerData fromHandle(Object handle) {
- return new WrappedVillagerData(handle);
- }
-
- private static ConstructorAccessor CONSTRUCTOR;
-
- public static WrappedVillagerData fromValues(Type type, Profession profession, int level) {
- Object genericType = TYPE_CONVERTER.getGeneric(type);
- Object genericProf = PROF_CONVERTER.getGeneric(profession);
-
- if (CONSTRUCTOR == null) {
- CONSTRUCTOR = Accessors.getConstructorAccessor(NMS_CLASS, TYPE_CLASS, PROF_CLASS, int.class);
- }
-
- Object handle = CONSTRUCTOR.invoke(genericType, genericProf, level);
- return fromHandle(handle);
- }
-
- public static Class> getNmsClass() {
- return NMS_CLASS;
- }
-
- public int getLevel() {
- return modifier.withType(int.class).read(0);
- }
-
- public Type getType() {
- return modifier.withType(TYPE_CLASS, TYPE_CONVERTER).read(0);
- }
-
- public Profession getProfession() {
- return modifier.withType(PROF_CLASS, PROF_CONVERTER).read(0);
- }
-
- @Override
- public WrappedVillagerData deepClone() {
- return WrappedVillagerData.fromValues(getType(), getProfession(), getLevel());
- }
-}
+package com.comphenix.protocol.wrappers;
+
+import com.comphenix.protocol.reflect.EquivalentConverter;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
+import com.comphenix.protocol.utility.MinecraftReflection;
+
+public class WrappedVillagerData extends AbstractWrapper implements ClonableWrapper {
+ private static final Class> NMS_CLASS = MinecraftReflection.getNullableNMS(
+ "world.entity.npc.VillagerData","VillagerData");
+ private static final Class> TYPE_CLASS = MinecraftReflection.getNullableNMS(
+ "world.entity.npc.VillagerType", "VillagerType");
+ private static final Class> PROF_CLASS = MinecraftReflection.getNullableNMS(
+ "world.entity.npc.VillagerProfession", "VillagerProfession");
+
+ private static EquivalentConverter TYPE_CONVERTER;
+ private static EquivalentConverter PROF_CONVERTER;
+
+ static {
+ if (NMS_CLASS != null) {
+ TYPE_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Type.class, TYPE_CLASS);
+ PROF_CONVERTER = new EnumWrappers.FauxEnumConverter<>(Profession.class, PROF_CLASS);
+ }
+ }
+
+ public enum Type {
+ DESERT, JUNGLE, PLAINS, SAVANNA, SNOW, SWAMP, TAIGA
+ }
+
+ public enum Profession {
+ NONE, ARMORER, BUTCHER, CARTOGRAPHER, CLERIC, FARMER, FISHERMAN,
+ FLETCHER, LEATHERWORKER, LIBRARIAN, MASON, NITWIT, SHEPHERD,
+ TOOLSMITH, WEAPONSMITH
+ }
+
+ private StructureModifier modifier;
+
+ private WrappedVillagerData(Object handle) {
+ super(NMS_CLASS);
+ setHandle(handle);
+
+ modifier = new StructureModifier<>(NMS_CLASS).withTarget(handle);
+ }
+
+ public static WrappedVillagerData fromHandle(Object handle) {
+ return new WrappedVillagerData(handle);
+ }
+
+ private static ConstructorAccessor CONSTRUCTOR;
+
+ public static WrappedVillagerData fromValues(Type type, Profession profession, int level) {
+ Object genericType = TYPE_CONVERTER.getGeneric(type);
+ Object genericProf = PROF_CONVERTER.getGeneric(profession);
+
+ if (CONSTRUCTOR == null) {
+ CONSTRUCTOR = Accessors.getConstructorAccessor(NMS_CLASS, TYPE_CLASS, PROF_CLASS, int.class);
+ }
+
+ Object handle = CONSTRUCTOR.invoke(genericType, genericProf, level);
+ return fromHandle(handle);
+ }
+
+ public static Class> getNmsClass() {
+ return NMS_CLASS;
+ }
+
+ public int getLevel() {
+ return modifier.withType(int.class).read(0);
+ }
+
+ public Type getType() {
+ return modifier.withType(TYPE_CLASS, TYPE_CONVERTER).read(0);
+ }
+
+ public Profession getProfession() {
+ return modifier.withType(PROF_CLASS, PROF_CONVERTER).read(0);
+ }
+
+ @Override
+ public WrappedVillagerData deepClone() {
+ return WrappedVillagerData.fromValues(getType(), getProfession(), getLevel());
+ }
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 3ab3c97a..4de8994d 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,45 +1,45 @@
-name: ProtocolLib
-version: ${project.fullVersion}
-description: Provides read/write access to the Minecraft protocol.
-authors: [dmulloy2, comphenix]
-
-main: com.comphenix.protocol.ProtocolLib
-load: STARTUP
-database: false
-api-version: "1.13"
-
-commands:
- protocol:
- description: Performs administrative tasks regarding ProtocolLib.
- usage: / config|check|update|timings|listeners|version|dump
- permission: protocol.admin
- permission-message: You don't have
- packet:
- description: Add or remove a simple packet listener.
- usage: / add|remove|names client|server [ID start]-[ID stop] [detailed]
- permission: protocol.admin
- permission-message: You don't have
- filter:
- description: Add or remove programmable filters to the packet listeners.
- usage: / add|remove name [ID start]-[ID stop]
- aliases: [packet_filter]
- permission: protocol.admin
- permission-message: You don't have
- packetlog:
- description: Logs hex representations of packets to a file or console
- usage: / [location]
- permission: protocol.admin
- permission-message: You don't have
-
-permissions:
- protocol.*:
- description: Gives access to everything.
- children:
- protocol.admin: true
- protocol.info: true
- protocol.admin:
- description: Able to initiate the update process, and can configure debug mode.
- default: op
- protocol.info:
- description: Can read update notifications and error reports.
+name: ProtocolLib
+version: ${project.fullVersion}
+description: Provides read/write access to the Minecraft protocol.
+authors: [dmulloy2, comphenix]
+
+main: com.comphenix.protocol.ProtocolLib
+load: STARTUP
+database: false
+api-version: "1.13"
+
+commands:
+ protocol:
+ description: Performs administrative tasks regarding ProtocolLib.
+ usage: / config|check|update|timings|listeners|version|dump
+ permission: protocol.admin
+ permission-message: You don't have
+ packet:
+ description: Add or remove a simple packet listener.
+ usage: / add|remove|names client|server [ID start]-[ID stop] [detailed]
+ permission: protocol.admin
+ permission-message: You don't have
+ filter:
+ description: Add or remove programmable filters to the packet listeners.
+ usage: / add|remove name [ID start]-[ID stop]
+ aliases: [packet_filter]
+ permission: protocol.admin
+ permission-message: You don't have
+ packetlog:
+ description: Logs hex representations of packets to a file or console
+ usage: / [location]
+ permission: protocol.admin
+ permission-message: You don't have
+
+permissions:
+ protocol.*:
+ description: Gives access to everything.
+ children:
+ protocol.admin: true
+ protocol.info: true
+ protocol.admin:
+ description: Able to initiate the update process, and can configure debug mode.
+ default: op
+ protocol.info:
+ description: Can read update notifications and error reports.
default: op
\ No newline at end of file
diff --git a/src/test/java/com/comphenix/protocol/BukkitInitialization.java b/src/test/java/com/comphenix/protocol/BukkitInitialization.java
index cf9e31ab..8811d818 100644
--- a/src/test/java/com/comphenix/protocol/BukkitInitialization.java
+++ b/src/test/java/com/comphenix/protocol/BukkitInitialization.java
@@ -1,126 +1,126 @@
-package com.comphenix.protocol;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.comphenix.protocol.reflect.accessors.Accessors;
-import com.comphenix.protocol.reflect.accessors.FieldAccessor;
-import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
-import java.util.Collections;
-import java.util.List;
-import net.minecraft.SharedConstants;
-import net.minecraft.core.IRegistry;
-import net.minecraft.server.DispenserRegistry;
-import net.minecraft.server.level.WorldServer;
-import org.apache.logging.log4j.LogManager;
-import org.bukkit.Bukkit;
-import org.bukkit.Server;
-import org.bukkit.World;
-import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
-import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
-import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemFactory;
-import org.bukkit.craftbukkit.v1_19_R1.util.Versioning;
-import org.spigotmc.SpigotWorldConfig;
-
-/**
- * Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
- *
- * @author Kristian
- */
-public class BukkitInitialization {
-
- private static final BukkitInitialization instance = new BukkitInitialization();
- private boolean initialized;
- private boolean packaged;
-
- private BukkitInitialization() {
- System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
- }
-
- /**
- * Statically initializes the mock server for unit testing
- */
- public static synchronized void initializeAll() {
- instance.initialize();
- }
-
- /**
- * Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
- */
- private void initialize() {
- if (!this.initialized) {
- // Denote that we're done
- this.initialized = true;
-
- try {
- LogManager.getLogger();
- } catch (Throwable ex) {
- // Happens only on my Jenkins, but if it errors here it works when it matters
- ex.printStackTrace();
- }
-
- instance.setPackage();
-
- SharedConstants.a();
- DispenserRegistry.a();
-
- try {
- IRegistry.class.getName();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
-
- String releaseTarget = SharedConstants.b().getReleaseTarget();
- String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
-
- // Mock the server object
- Server mockedServer = mock(Server.class);
-
- when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
- when(mockedServer.getName()).thenReturn("Mock Server");
- when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
- when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
-
- when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
- when(mockedServer.isPrimaryThread()).thenReturn(true);
-
- WorldServer nmsWorld = mock(WorldServer.class);
-
- SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
-
- try {
- FieldAccessor spigotConfig = Accessors.getFieldAccessor(nmsWorld.getClass().getField("spigotConfig"));
- spigotConfig.set(nmsWorld, mockWorldConfig);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException(ex);
- }
-
- CraftWorld world = mock(CraftWorld.class);
- when(world.getHandle()).thenReturn(nmsWorld);
-
- List worlds = Collections.singletonList(world);
- when(mockedServer.getWorlds()).thenReturn(worlds);
-
- // Inject this fake server
- Bukkit.setServer(mockedServer);
- }
- }
-
- /**
- * Ensure that package names are correctly set up.
- */
- private void setPackage() {
- if (!this.packaged) {
- this.packaged = true;
-
- try {
- LogManager.getLogger();
- } catch (Throwable ex) {
- // Happens only on my Jenkins, but if it errors here it works when it matters
- ex.printStackTrace();
- }
-
- MinecraftReflectionTestUtil.init();
- }
- }
+package com.comphenix.protocol;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.comphenix.protocol.reflect.accessors.Accessors;
+import com.comphenix.protocol.reflect.accessors.FieldAccessor;
+import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
+import java.util.Collections;
+import java.util.List;
+import net.minecraft.SharedConstants;
+import net.minecraft.core.IRegistry;
+import net.minecraft.server.DispenserRegistry;
+import net.minecraft.server.level.WorldServer;
+import org.apache.logging.log4j.LogManager;
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemFactory;
+import org.bukkit.craftbukkit.v1_19_R1.util.Versioning;
+import org.spigotmc.SpigotWorldConfig;
+
+/**
+ * Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
+ *
+ * @author Kristian
+ */
+public class BukkitInitialization {
+
+ private static final BukkitInitialization instance = new BukkitInitialization();
+ private boolean initialized;
+ private boolean packaged;
+
+ private BukkitInitialization() {
+ System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
+ }
+
+ /**
+ * Statically initializes the mock server for unit testing
+ */
+ public static synchronized void initializeAll() {
+ instance.initialize();
+ }
+
+ /**
+ * Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
+ */
+ private void initialize() {
+ if (!this.initialized) {
+ // Denote that we're done
+ this.initialized = true;
+
+ try {
+ LogManager.getLogger();
+ } catch (Throwable ex) {
+ // Happens only on my Jenkins, but if it errors here it works when it matters
+ ex.printStackTrace();
+ }
+
+ instance.setPackage();
+
+ SharedConstants.a();
+ DispenserRegistry.a();
+
+ try {
+ IRegistry.class.getName();
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+
+ String releaseTarget = SharedConstants.b().getReleaseTarget();
+ String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
+
+ // Mock the server object
+ Server mockedServer = mock(Server.class);
+
+ when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
+ when(mockedServer.getName()).thenReturn("Mock Server");
+ when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
+ when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
+
+ when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
+ when(mockedServer.isPrimaryThread()).thenReturn(true);
+
+ WorldServer nmsWorld = mock(WorldServer.class);
+
+ SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
+
+ try {
+ FieldAccessor spigotConfig = Accessors.getFieldAccessor(nmsWorld.getClass().getField("spigotConfig"));
+ spigotConfig.set(nmsWorld, mockWorldConfig);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ CraftWorld world = mock(CraftWorld.class);
+ when(world.getHandle()).thenReturn(nmsWorld);
+
+ List worlds = Collections.singletonList(world);
+ when(mockedServer.getWorlds()).thenReturn(worlds);
+
+ // Inject this fake server
+ Bukkit.setServer(mockedServer);
+ }
+ }
+
+ /**
+ * Ensure that package names are correctly set up.
+ */
+ private void setPackage() {
+ if (!this.packaged) {
+ this.packaged = true;
+
+ try {
+ LogManager.getLogger();
+ } catch (Throwable ex) {
+ // Happens only on my Jenkins, but if it errors here it works when it matters
+ ex.printStackTrace();
+ }
+
+ MinecraftReflectionTestUtil.init();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/comphenix/protocol/PacketTypeTest.java b/src/test/java/com/comphenix/protocol/PacketTypeTest.java
index ace2adaf..2fa60808 100644
--- a/src/test/java/com/comphenix/protocol/PacketTypeTest.java
+++ b/src/test/java/com/comphenix/protocol/PacketTypeTest.java
@@ -1,334 +1,334 @@
-/**
- * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
- *
- * 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;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import com.comphenix.protocol.PacketType.Protocol;
-import com.comphenix.protocol.PacketType.Sender;
-import com.comphenix.protocol.events.PacketContainer;
-import com.comphenix.protocol.injector.packet.PacketRegistry;
-import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
-import net.minecraft.network.EnumProtocol;
-import net.minecraft.network.protocol.EnumProtocolDirection;
-import net.minecraft.network.protocol.login.PacketLoginInStart;
-import org.apache.commons.lang.WordUtils;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-/**
- * @author dmulloy2
- */
-public class PacketTypeTest {
-
- @BeforeAll
- public static void beforeClass() {
- BukkitInitialization.initializeAll();
-
- // I'm well aware this is jank, but it does in fact work correctly and give the desired result
- PacketType.onDynamicCreate = className -> {
- throw new RuntimeException("Dynamically generated packet " + className);
- };
- }
-
- @AfterAll
- public static void afterClass() {
- PacketType.onDynamicCreate = __ -> {
- };
- }
-
- @SuppressWarnings("unchecked")
- public static void main(String[] args) throws Exception {
- MinecraftReflectionTestUtil.init();
-
- Set> allTypes = new HashSet<>();
- List> newTypes = new ArrayList<>();
-
- EnumProtocol[] protocols = EnumProtocol.values();
- for (EnumProtocol protocol : protocols) {
- System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
-
- Field field = EnumProtocol.class.getDeclaredField("j");
- field.setAccessible(true);
-
- Map map = (Map) field.get(protocol);
- for (Entry entry : map.entrySet()) {
- Field mapField = entry.getValue().getClass().getDeclaredField("b");
- mapField.setAccessible(true);
-
- Map, Integer> reverseMap = (Map, Integer>) mapField.get(entry.getValue());
-
- Map> treeMap = new TreeMap<>();
- for (Entry, Integer> entry1 : reverseMap.entrySet()) {
- treeMap.put(entry1.getValue(), entry1.getKey());
- }
-
- System.out.println(" " + entry.getKey());
- for (Entry> entry1 : treeMap.entrySet()) {
- System.out.println(generateNewType(entry1.getKey(), entry1.getValue()));
- allTypes.add(entry1.getValue());
-
- try {
- PacketType.fromClass(entry1.getValue());
- } catch (Exception ex) {
- newTypes.add(entry1.getValue());
- }
- }
- }
- }
-
- System.out.println("New types: " + newTypes);
-
- for (PacketType type : PacketType.values()) {
- if (type.isDeprecated()) {
- continue;
- }
-
- if (!allTypes.contains(type.getPacketClass())) {
- System.out.println(type + " was removed");
- }
- }
- }
-
- private static String formatHex(int dec) {
- if (dec < 0) {
- return "0xFF";
- }
-
- String hex = Integer.toHexString(dec).toUpperCase();
- return "0x" + (hex.length() < 2 ? "0" : "") + hex;
- }
-
- private static List splitOnCaps(String string) {
- List list = new ArrayList<>();
- StringBuilder builder = new StringBuilder();
- char[] chars = string.toCharArray();
- for (int i = 0; i < chars.length; i++) {
- char c = chars[i];
- if (i != 0 && Character.isUpperCase(c)) {
- list.add(builder.toString());
- builder = new StringBuilder();
- }
-
- builder.append(c);
- }
-
- list.add(builder.toString());
- return list;
- }
-
- private static String generateNewType(int packetId, Class> clazz) {
- StringBuilder builder = new StringBuilder();
- builder.append("\t\t\t");
- builder.append("public static final PacketType ");
-
- String fullName = clazz.getName();
- fullName = fullName.substring(fullName.lastIndexOf(".") + 1);
-
- String className;
- List classNames = new ArrayList<>();
-
- if (fullName.endsWith("Packet")) {
- for (String name : fullName.split("\\$")) {
- List split = splitOnCaps(name);
- StringBuilder nameBuilder = new StringBuilder();
- for (int i = 1; i < split.size() - 1; i++) {
- nameBuilder.append(split.get(i));
- }
- classNames.add(nameBuilder.toString());
- }
- } else {
- for (String name : fullName.split("\\$")) {
- List split = splitOnCaps(name);
- StringBuilder nameBuilder = new StringBuilder();
- for (int i = 3; i < split.size(); i++) {
- nameBuilder.append(split.get(i));
- }
- classNames.add(nameBuilder.toString());
- }
- }
-
- className = classNames.get(classNames.size() - 1);
-
- // Format it like SET_PROTOCOL
- StringBuilder fieldName = new StringBuilder();
- char[] chars = className.toCharArray();
- for (int i = 0; i < chars.length; i++) {
- char c = chars[i];
- if (i != 0 && Character.isUpperCase(c)) {
- fieldName.append("_");
- }
- fieldName.append(Character.toUpperCase(c));
- }
-
- builder.append(fieldName.toString().replace("N_B_T", "NBT"));
- builder.append(" = ");
-
- // Add spacing
- if (builder.length() > 65) {
- builder.append("\n");
- } else {
- while (builder.length() < 65) {
- builder.append(" ");
- }
- }
- builder.append("new ");
- builder.append("PacketType(PROTOCOL, SENDER, ");
-
- builder.append(formatHex(packetId));
- builder.append(", ");
-
- StringBuilder nameBuilder = new StringBuilder();
- for (int i = 0; i < classNames.size(); i++) {
- if (i != 0) {
- nameBuilder.append("$");
- }
- nameBuilder.append(classNames.get(i));
- }
-
- String name = nameBuilder.toString();
- String namesArg = listToString(getAllNames(clazz, name));
-
- builder.append(namesArg);
- builder.append(");");
-
- return builder.toString();
- }
-
- private static List getAllNames(Class> packetClass, String newName) {
- List names = new ArrayList<>();
- names.add(newName);
-
- try {
- PacketType type = PacketType.fromClass(packetClass);
- for (String alias : type.names) {
- alias = alias.substring(alias.lastIndexOf('.') + 1);
- if (!names.contains(alias)) {
- names.add(alias);
- }
- }
- } catch (Exception ignored) {
- }
-
- return names;
- }
-
- private static String listToString(List list) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < list.size(); i++) {
- if (i != 0) {
- builder.append(", ");
- }
- builder.append("\"").append(list.get(i)).append("\"");
- }
-
- return builder.toString();
- }
-
- @BeforeAll
- public static void initializeReflection() {
- BukkitInitialization.initializeAll();
- }
-
- @Test
- public void testFindCurrent() {
- assertEquals(PacketType.Play.Client.STEER_VEHICLE,
- PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
- }
-
- @Test
- public void testLoginStart() {
- // This packet is critical for handleLoin
- assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
- }
-
- @Test
- public void testDeprecation() {
- assertTrue(PacketType.Status.Server.OUT_SERVER_INFO.isDeprecated(), "Packet isn't properly deprecated");
- assertTrue(PacketRegistry.getServerPacketTypes().contains(PacketType.Status.Server.OUT_SERVER_INFO),
- "Deprecated packet isn't properly included");
- assertFalse(PacketType.Play.Server.CHAT.isDeprecated(), "Packet isn't properly deprecated");
- assertEquals(PacketType.Status.Server.OUT_SERVER_INFO, PacketType.Status.Server.SERVER_INFO,
- "Deprecated packets aren't equal");
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void ensureTypesAreCorrect() throws Exception {
- boolean fail = false;
-
- EnumProtocol[] protocols = EnumProtocol.values();
- for (EnumProtocol protocol : protocols) {
- Field field = EnumProtocol.class.getDeclaredField("j");
- field.setAccessible(true);
-
- Map map = (Map) field.get(protocol);
- for (Entry entry : map.entrySet()) {
- Field mapField = entry.getValue().getClass().getDeclaredField("b");
- mapField.setAccessible(true);
-
- Map, Integer> reverseMap = (Map, Integer>) mapField.get(entry.getValue());
-
- Map> treeMap = new TreeMap<>();
- for (Entry, Integer> entry1 : reverseMap.entrySet()) {
- treeMap.put(entry1.getValue(), entry1.getKey());
- }
-
- for (Entry> entry1 : treeMap.entrySet()) {
- try {
- PacketType type = PacketType.fromClass(entry1.getValue());
- if (type.getCurrentId() != entry1.getKey()) {
- throw new IllegalStateException(
- "Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
- + type.getCurrentId());
- }
- } catch (Throwable ex) {
- ex.printStackTrace();
- fail = true;
- }
- }
- }
- }
-
- assertFalse(fail, "Packet type(s) were incorrect!");
- }
-
- @Test
- public void testPacketCreation() {
- boolean fail = false;
- for (PacketType type : PacketType.values()) {
- if (type.isSupported()) {
- try {
- new PacketContainer(type);
- } catch (Exception ex) {
- ex.printStackTrace();
- fail = true;
- }
- }
- }
- assertFalse(fail, "Packet type(s) failed to instantiate");
- }
-}
+/**
+ * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2016 dmulloy2
+ *
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.comphenix.protocol.PacketType.Protocol;
+import com.comphenix.protocol.PacketType.Sender;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.injector.packet.PacketRegistry;
+import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import net.minecraft.network.EnumProtocol;
+import net.minecraft.network.protocol.EnumProtocolDirection;
+import net.minecraft.network.protocol.login.PacketLoginInStart;
+import org.apache.commons.lang.WordUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author dmulloy2
+ */
+public class PacketTypeTest {
+
+ @BeforeAll
+ public static void beforeClass() {
+ BukkitInitialization.initializeAll();
+
+ // I'm well aware this is jank, but it does in fact work correctly and give the desired result
+ PacketType.onDynamicCreate = className -> {
+ throw new RuntimeException("Dynamically generated packet " + className);
+ };
+ }
+
+ @AfterAll
+ public static void afterClass() {
+ PacketType.onDynamicCreate = __ -> {
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void main(String[] args) throws Exception {
+ MinecraftReflectionTestUtil.init();
+
+ Set> allTypes = new HashSet<>();
+ List> newTypes = new ArrayList<>();
+
+ EnumProtocol[] protocols = EnumProtocol.values();
+ for (EnumProtocol protocol : protocols) {
+ System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
+
+ Field field = EnumProtocol.class.getDeclaredField("j");
+ field.setAccessible(true);
+
+ Map map = (Map