From 80f4c7b9a78af844a8cbb15e354b6692c128d4a0 Mon Sep 17 00:00:00 2001 From: Tarrant Date: Sun, 26 Jul 2020 18:33:05 -0500 Subject: [PATCH] Support for MerchantRecipeList (#894) * Added support for MerchantRecipeList found in OPEN_WINDOW_MERCHANT of MC versions 1.13+ --- .../protocol/events/PacketContainer.java | 12 +++++ .../protocol/wrappers/BukkitConverters.java | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/src/main/java/com/comphenix/protocol/events/PacketContainer.java index 6c742a46..458e6b68 100644 --- a/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -61,6 +61,7 @@ import org.bukkit.WorldType; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; @@ -937,6 +938,17 @@ public class PacketContainer implements Serializable { BukkitConverters.getDimensionIDConverter() ); } + + /** + * Retrieve a read/write structure for the MerchantRecipeList class. + * @return A modifier for MerchantRecipeList fields. + */ + public StructureModifier> getMerchantRecipeLists() { + return structureModifier.withType( + MinecraftReflection.getMinecraftClass("MerchantRecipeList"), + BukkitConverters.getMerchantRecipeListConverter() + ); + } /** * Retrieve a read/write structure for ItemSlot/ItemStack pair lists in 1.16+ diff --git a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index 81a88c3f..7302d6d2 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; +import java.util.stream.Collectors; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; @@ -38,6 +39,7 @@ import com.comphenix.protocol.reflect.FieldAccessException; 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.ConstructorAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; @@ -60,6 +62,7 @@ import org.bukkit.advancement.Advancement; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; @@ -1237,4 +1240,54 @@ public class BukkitConverters { } }); } + + private static ConstructorAccessor merchantRecipeListConstructor = null; + private static MethodAccessor bukkitMerchantRecipeToCraft = null; + private static MethodAccessor craftMerchantRecipeToNMS = null; + private static MethodAccessor nmsMerchantRecipeToBukkit = null; + + /** + * Creates a converter from a MerchantRecipeList (which is just an ArrayList of MerchantRecipe wrapper) + * to a {@link List} of {@link MerchantRecipe}. Primarily for the packet OPEN_WINDOW_MERCHANT which is present + * in 1.13+. + * + * @return The MerchantRecipeList converter. + */ + public static EquivalentConverter> getMerchantRecipeListConverter() { + return ignoreNull(new EquivalentConverter>() { + + @Override + public Object getGeneric(List specific) { + if (merchantRecipeListConstructor == null) { + Class merchantRecipeListClass = MinecraftReflection.getMinecraftClass("MerchantRecipeList"); + merchantRecipeListConstructor = Accessors.getConstructorAccessor(merchantRecipeListClass); + Class craftMerchantRecipeClass = MinecraftReflection.getCraftBukkitClass("inventory.CraftMerchantRecipe"); + FuzzyReflection reflection = FuzzyReflection.fromClass(craftMerchantRecipeClass, false); + bukkitMerchantRecipeToCraft = Accessors.getMethodAccessor(reflection.getMethodByName("fromBukkit")); + craftMerchantRecipeToNMS = Accessors.getMethodAccessor(reflection.getMethodByName("toMinecraft")); + } + return specific.stream().map(recipe -> craftMerchantRecipeToNMS.invoke(bukkitMerchantRecipeToCraft.invoke(null, recipe))) + .collect(() -> (List)merchantRecipeListConstructor.invoke(), List::add, List::addAll); + } + + @Override + public List getSpecific(Object generic) { + if (nmsMerchantRecipeToBukkit == null) { + Class merchantRecipeClass = MinecraftReflection.getMinecraftClass("MerchantRecipe"); + FuzzyReflection reflection = FuzzyReflection.fromClass(merchantRecipeClass, false); + nmsMerchantRecipeToBukkit = Accessors.getMethodAccessor(reflection.getMethodByName("asBukkit")); + } + return ((List)generic).stream().map(o -> (MerchantRecipe)nmsMerchantRecipeToBukkit.invoke(o)).collect(Collectors.toList()); + } + + @Override + public Class> getSpecificType() { + // Damn you Java + Class dummy = List.class; + return (Class>) dummy; + } + + }); + } + }