From 881b8e9b905e99145d6be28f9b0144fbc18aa82a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:37:31 -0500 Subject: [PATCH] Better multi-item recipe support for 1.21.1 --- .../java/JavaRecipeBookAddTranslator.java | 77 ++++++++++++++++--- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java index 05be7db3d..71fd4a200 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRecipeBookAddTranslator.java @@ -30,6 +30,7 @@ import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.RecipeUnlockingRequirement; import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData; @@ -72,6 +73,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.WithRem import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundRecipeBookAddPacket; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -202,11 +204,39 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator translateToInput(session, subDisplay)) - .filter(Objects::nonNull) - .flatMap(List::stream) - .toList(); + + // Try and see if the contents match a tag. + // ViaVersion maps pre-1.21.2 ingredient lists to CompositeSlotDisplays. + int[] items = new int[composite.contents().size()]; + List contents = composite.contents(); + for (int i = 0; i < contents.size(); i++) { + SlotDisplay subDisplay = contents.get(i); + int id; + if (subDisplay instanceof ItemSlotDisplay item) { + id = item.item(); + } else if (!(subDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay)) { + id = -1; + } else if (itemStackSlotDisplay.itemStack().getAmount() == 1 + && itemStackSlotDisplay.itemStack().getDataComponents() == null) { + id = itemStackSlotDisplay.itemStack().getId(); + } else { + id = -1; + } + if (id == -1) { + // We couldn't guarantee a "normal" item from this stack. + return fallbackCompositeMapping(session, composite); + } + items[i] = id; + } + // For searching in the tag map. + Arrays.sort(items); + + List tagDescriptor = lookupBedrockTag(session, items); + if (tagDescriptor != null) { + return tagDescriptor; + } + + return fallbackCompositeMapping(session, composite); } if (slotDisplay instanceof WithRemainderSlotDisplay remainder) { // Don't need to worry about what will stay in the crafting table after crafting for the purposes of sending recipes to Bedrock @@ -230,12 +260,9 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator { - var bedrockTags = Registries.TAGS.forVersion(session.getUpstream().getProtocolVersion()); - String bedrockTag = bedrockTags.get(key); - if (bedrockTag != null) { - return Collections.singletonList( - new ItemDescriptorWithCount(new ItemTagDescriptor(bedrockTag), 1) - ); + List tagDescriptor = lookupBedrockTag(session, key); + if (tagDescriptor != null) { + return tagDescriptor; } // In the future, we can probably search through and use subsets of tags as well. @@ -278,6 +305,33 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator lookupBedrockTag(GeyserSession session, int[] items) { + var bedrockTags = Registries.TAGS.forVersion(session.getUpstream().getProtocolVersion()); + String bedrockTag = bedrockTags.get(items); + if (bedrockTag != null) { + return Collections.singletonList( + new ItemDescriptorWithCount(new ItemTagDescriptor(bedrockTag), 1) + ); + } else { + return null; + } + } + + /** + * Converts CompositeSlotDisplay contents to a list of basic ItemDescriptorWithCounts. + */ + private List fallbackCompositeMapping(GeyserSession session, CompositeSlotDisplay composite) { + return composite.contents().stream() + .map(subDisplay -> translateToInput(session, subDisplay)) + .filter(Objects::nonNull) + .flatMap(List::stream) + .toList(); + } + private Pair>, ItemData> combinations(GeyserSession session, RecipeDisplay display, List ingredients) { Pair pair = translateToOutput(session, display.result()); if (pair == null || !pair.right().isValid()) { @@ -327,6 +381,7 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator descriptors.get(0)).toList()), output