From aabe47f8bc5e737d25f7d6e867f994314581fa2c Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Thu, 3 Oct 2024 20:52:56 +0200 Subject: [PATCH] Start working on recipe rewriting --- .../viaversion/api/minecraft/HolderSet.java | 4 +- .../api/minecraft/HolderSetImpl.java | 83 +++++---- .../template/BlockItemPacketRewriter1_99.java | 10 +- .../BlockItemPacketRewriter1_21_2.java | 2 +- .../rewriter/RecipeRewriter.java | 94 ---------- .../rewriter/RecipeRewriter1_21_2.java | 170 +++++++++++++---- .../rewriter/RecipeDisplayRewriter.java | 175 ++++++++++++++++++ .../viaversion/rewriter/RecipeRewriter.java | 13 +- 8 files changed, 367 insertions(+), 184 deletions(-) delete mode 100644 common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java create mode 100644 common/src/main/java/com/viaversion/viaversion/rewriter/RecipeDisplayRewriter.java diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java index 087cdf07b..ccd0e1d2f 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSet.java @@ -36,7 +36,7 @@ public interface HolderSet { * @return a new holder set */ static HolderSet of(final String tagKey) { - return new HolderSetImpl(tagKey); + return new HolderSetImpl.Tag(tagKey); } /** @@ -46,7 +46,7 @@ public interface HolderSet { * @return a new holder set */ static HolderSet of(final int[] ids) { - return new HolderSetImpl(ids); + return new HolderSetImpl.Ids(ids); } /** diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java index 5557da917..5c9c5d095 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/HolderSetImpl.java @@ -22,50 +22,63 @@ */ package com.viaversion.viaversion.api.minecraft; -import com.viaversion.viaversion.util.EitherImpl; import it.unimi.dsi.fastutil.ints.Int2IntFunction; +import java.util.Arrays; -final class HolderSetImpl extends EitherImpl implements HolderSet { +final class HolderSetImpl { - HolderSetImpl(final String tagKey) { - super(tagKey, null); - } + record Tag(String tagKey) implements HolderSet { + @Override + public boolean hasTagKey() { + return true; + } - HolderSetImpl(final int[] ids) { - super(null, ids); - } + @Override + public int[] ids() { + throw new IllegalArgumentException("This holder set has a tag key"); + } - @Override - public String tagKey() { - return left(); - } + @Override + public boolean hasIds() { + return false; + } - @Override - public boolean hasTagKey() { - return isLeft(); - } - - @Override - public int[] ids() { - return right(); - } - - @Override - public boolean hasIds() { - return isRight(); - } - - @Override - public HolderSet rewrite(final Int2IntFunction idRewriter) { - if (hasTagKey()) { + @Override + public HolderSet rewrite(final Int2IntFunction idRewriter) { return this; } + } - final int[] ids = ids(); - final int[] mappedIds = new int[ids.length]; - for (int i = 0; i < mappedIds.length; i++) { - mappedIds[i] = idRewriter.applyAsInt(ids[i]); + record Ids(int[] ids) implements HolderSet { + @Override + public boolean hasTagKey() { + return false; + } + + @Override + public String tagKey() { + throw new IllegalArgumentException("This holder set has direct ids"); + } + + @Override + public boolean hasIds() { + return true; + } + + @Override + public HolderSet rewrite(final Int2IntFunction idRewriter) { + final int[] mappedIds = new int[ids.length]; + for (int i = 0; i < mappedIds.length; i++) { + mappedIds[i] = idRewriter.applyAsInt(ids[i]); + } + return new Ids(mappedIds); + } + + @Override + public String toString() { + return "Ids{" + + "ids=" + Arrays.toString(ids) + + '}'; } - return new HolderSetImpl(mappedIds); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/template/BlockItemPacketRewriter1_99.java b/common/src/main/java/com/viaversion/viaversion/protocols/template/BlockItemPacketRewriter1_99.java index ac066f88f..fe33bd3df 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/template/BlockItemPacketRewriter1_99.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/template/BlockItemPacketRewriter1_99.java @@ -23,7 +23,7 @@ import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPac import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5; import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21; import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21; -import com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.RecipeRewriter1_21_2; +import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter; import com.viaversion.viaversion.rewriter.BlockRewriter; import com.viaversion.viaversion.rewriter.StructuredItemRewriter; @@ -70,9 +70,11 @@ final class BlockItemPacketRewriter1_99 extends StructuredItemRewriter(protocol).register1_20_5(ClientboundPackets1_21.UPDATE_RECIPES); + final RecipeDisplayRewriter recipeRewriter = new RecipeDisplayRewriter<>(protocol); + recipeRewriter.registerUpdateRecipes(ClientboundPackets1_21.UPDATE_RECIPES); + //recipeRewriter.registerRecipeBookAdd(ClientboundPackets1_21.RECIPE_BOOK_ADD); + recipeRewriter.registerPlaceGhostRecipe(ClientboundPackets1_21.PLACE_GHOST_RECIPE); // OR do this if serialization of recipes changed and override the relevant method - // Add new serializers to RecipeRewriter, or extend the last one for changes - // new RecipeRewriter1_21_2(this) {}.register1_20_5(ClientboundPackets1_21.DECLARE_RECIPES); + // Add new serializers to RecipeDisplayRewriter, or extend the last one for changes } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java index e0211538d..6d58feb2d 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java @@ -193,7 +193,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< }); protocol.registerClientbound(ClientboundPackets1_21.UPDATE_RECIPES, wrapper -> { - final RecipeRewriter rewriter = new RecipeRewriter(protocol); + final RecipeRewriter1_21_2 rewriter = new RecipeRewriter1_21_2(protocol); // Holds state, create a new one for each packet wrapper.cancel(); // TODO final int size = wrapper.passthrough(Types.VAR_INT); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java deleted file mode 100644 index 9dc50a1cf..000000000 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion - * Copyright (C) 2016-2024 ViaVersion and contributors - * - * 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 3 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, see . - */ -package com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter; - -import com.viaversion.viaversion.api.minecraft.HolderSet; -import com.viaversion.viaversion.api.minecraft.item.Item; -import com.viaversion.viaversion.api.protocol.Protocol; -import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; -import com.viaversion.viaversion.api.type.Types; -import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3; -import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21; - -final class RecipeRewriter extends RecipeRewriter1_20_3 { - - RecipeRewriter(final Protocol protocol) { - super(protocol); - } - - @Override - protected void handleIngredient(final PacketWrapper wrapper) { - wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); - } - - @Override - public void handleCraftingShaped(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - final int width = wrapper.passthrough(Types.VAR_INT); - final int height = wrapper.passthrough(Types.VAR_INT); - final int ingredients = width * height; - - wrapper.write(Types.VAR_INT, ingredients); - for (int i = 0; i < ingredients; i++) { - wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); - } - - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); // Result - wrapper.passthrough(Types.BOOLEAN); // Show notification - } - - @Override - public void handleCraftingShapeless(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - - final int ingredients = wrapper.read(Types.VAR_INT); - final HolderSet[] ingredient = new HolderSet[ingredients]; - for (int i = 0; i < ingredients; i++) { - ingredient[i] = ingredient(wrapper); - } - - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); - - // Also moved below here - wrapper.write(Types.VAR_INT, ingredients); - for (final HolderSet item : ingredient) { - wrapper.write(Types.HOLDER_SET, item); - } - } - - private HolderSet ingredient(final PacketWrapper wrapper) { - final Item[] items = wrapper.read(itemArrayType()); - final int[] ids = new int[items.length]; - for (int i = 0; i < items.length; i++) { - final Item item = rewrite(wrapper.user(), items[i]); - ids[i] = item.identifier(); - } - return HolderSet.of(ids); - } - - @Override - public void handleRecipeType(final PacketWrapper wrapper, final String type) { - if (type.equals("crafting_special_suspiciousstew") || type.equals("crafting_special_shulkerboxcoloring")) { - wrapper.read(Types.VAR_INT); // Crafting book category - } else { - super.handleRecipeType(wrapper, type); - } - } -} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java index d58c2b944..364a7db3a 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java @@ -17,63 +17,161 @@ */ package com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter; -import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.minecraft.HolderSet; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.protocol.Protocol; -import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import java.util.ArrayList; +import java.util.List; -// Dead as of 1.21.2, the server only sends simple recipe display data instead of full results -public class RecipeRewriter1_21_2 extends RecipeRewriter1_20_3 { +final class RecipeRewriter1_21_2 extends RecipeRewriter1_20_3 { - public RecipeRewriter1_21_2(final Protocol protocol) { + private final List shapelessRecipes = new ArrayList<>(); + private final List shapedRecipes = new ArrayList<>(); + private final List smithingRecipes = new ArrayList<>(); + private final List stoneCutterRecipes = new ArrayList<>(); + private final List simpleRecipes = new ArrayList<>(); + private final Object2IntMap recipeGroups = new Object2IntOpenHashMap<>(); + // TODO + private final IntList smithingAddition = new IntArrayList(); + private final IntList smithingTemplate = new IntArrayList(); + private final IntList smithingBase = new IntArrayList(); + private final IntList furnaceInput = new IntArrayList(); + private final IntList smokerInput = new IntArrayList(); + private final IntList blastFurnaceInput = new IntArrayList(); + private final IntList campfireInput = new IntArrayList(); + + record SimpleRecipe(int category) { + } + + record ShapelessRecipe(int group, int category, HolderSet[] ingredients, Item result) { + } + + record ShapedRecipe(int group, int category, int width, int height, HolderSet[] ingredients, Item result) { + } + + record StoneCutterRecipe(int group, HolderSet ingredient, Item result) { + } + + record SmithingRecipe(HolderSet ingredient, Item result) { + } + + RecipeRewriter1_21_2(final Protocol protocol) { super(protocol); + // TODO ? + recipeHandlers.put("smelting", wrapper -> handleSmelting(wrapper, furnaceInput)); + recipeHandlers.put("blasting", wrapper -> handleSmelting(wrapper, blastFurnaceInput)); + recipeHandlers.put("smoking", wrapper -> handleSmelting(wrapper, smokerInput)); + recipeHandlers.put("campfire_cooking", wrapper -> handleSmelting(wrapper, campfireInput)); + recipeGroups.defaultReturnValue(-1); + } + + private HolderSet readIngredient(final PacketWrapper wrapper) { + // Ingredients are no longer full items, already store them as just holder sets + final Item[] items = wrapper.read(itemArrayType()); + final int[] ids = new int[items.length]; + final MappingData mappings = protocol.getMappingData(); + for (int i = 0; i < items.length; i++) { + final Item item = items[i]; + ids[i] = mappings.getNewItemId(item.identifier()); + } + return HolderSet.of(ids); + } + + private void readIngredientToList(final PacketWrapper wrapper, final IntCollection list) { + final Item[] items = wrapper.read(itemArrayType()); + final MappingData mappings = protocol.getMappingData(); + for (final Item item : items) { + final int mappedId = mappings.getNewItemId(item.identifier()); + list.add(mappedId); + } + } + + @Override + public void handleSimpleRecipe(final PacketWrapper wrapper) { + final int category = wrapper.read(Types.VAR_INT); + simpleRecipes.add(new SimpleRecipe(category)); + } + + @Override + public void handleStonecutting(final PacketWrapper wrapper) { + final int group = recipeGroupId(wrapper.read(Types.STRING)); + final HolderSet ingredient = readIngredient(wrapper); + final Item result = rewrite(wrapper.user(), wrapper.read(itemType())); + stoneCutterRecipes.add(new StoneCutterRecipe(group, ingredient, result)); + } + + private int recipeGroupId(final String recipeGroup) { + final int size = recipeGroups.size(); + final int value = recipeGroups.putIfAbsent(recipeGroup, size); + return value != -1 ? value : size; + } + + @Override + public void handleSmithingTransform(final PacketWrapper wrapper) { + final IntList template = new IntArrayList(); + readIngredientToList(wrapper, template); + smithingTemplate.addAll(template); + readIngredientToList(wrapper, smithingBase); + readIngredientToList(wrapper, smithingAddition); + final Item result = rewrite(wrapper.user(), wrapper.read(itemType())); + smithingRecipes.add(new SmithingRecipe(HolderSet.of(template.toIntArray()), result)); // TODO ? + } + + @Override + public void handleSmithingTrim(final PacketWrapper wrapper) { + readIngredientToList(wrapper, smithingTemplate); + readIngredientToList(wrapper, smithingBase); + readIngredientToList(wrapper, smithingAddition); } @Override public void handleCraftingShaped(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - wrapper.passthrough(Types.VAR_INT); // Width - wrapper.passthrough(Types.VAR_INT); // Height - - final int ingredients = wrapper.passthrough(Types.VAR_INT); - for (int i = 0; i < ingredients; i++) { - handleIngredient(wrapper); + final int group = recipeGroupId(wrapper.read(Types.STRING)); + final int category = wrapper.read(Types.VAR_INT); + final int width = wrapper.read(Types.VAR_INT); + final int height = wrapper.read(Types.VAR_INT); + final int ingredientsSize = width * height; + final HolderSet[] ingredients = new HolderSet[ingredientsSize]; + for (int i = 0; i < ingredientsSize; i++) { + ingredients[i] = readIngredient(wrapper); } + final Item result = rewrite(wrapper.user(), wrapper.read(itemType())); + shapedRecipes.add(new ShapedRecipe(group, category, width, height, ingredients, result)); - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); // Result - wrapper.passthrough(Types.BOOLEAN); // Show notification + wrapper.read(Types.BOOLEAN); // Show notification } @Override public void handleCraftingShapeless(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); // Result - handleIngredients(wrapper); + final int group = recipeGroupId(wrapper.read(Types.STRING)); + final int category = wrapper.read(Types.VAR_INT); + final int ingredientsSize = wrapper.read(Types.VAR_INT); + final HolderSet[] ingredients = new HolderSet[ingredientsSize]; + for (int i = 0; i < ingredientsSize; i++) { + ingredients[i] = readIngredient(wrapper); + } + + final Item result = rewrite(wrapper.user(), wrapper.read(itemType())); + shapelessRecipes.add(new ShapelessRecipe(group, category, ingredients, result)); } - @Override - protected void handleIngredient(final PacketWrapper wrapper) { - final HolderSet items = wrapper.passthrough(Types.HOLDER_SET); - if (items.hasTagKey()) { - return; - } + private void handleSmelting(final PacketWrapper wrapper, final IntCollection list) { + final int group = recipeGroupId(wrapper.read(Types.STRING)); + final int category = wrapper.read(Types.VAR_INT); + readIngredientToList(wrapper, list); + wrapper.read(itemType()); // Result - final int[] ids = items.ids(); - for (int i = 0; i < ids.length; i++) { - ids[i] = rewriteItemId(wrapper.user(), ids[i]); - } - } - - protected int rewriteItemId(final UserConnection connection, final int id) { - if (protocol.getMappingData() != null && protocol.getMappingData().getItemMappings() != null) { - return protocol.getMappingData().getItemMappings().getNewIdOrDefault(id, id); - } - return id; + wrapper.read(Types.FLOAT); // EXP + wrapper.read(Types.VAR_INT); // Cooking time } } diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeDisplayRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeDisplayRewriter.java new file mode 100644 index 000000000..71b68f151 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeDisplayRewriter.java @@ -0,0 +1,175 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * 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 3 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, see . + */ +package com.viaversion.viaversion.rewriter; + +import com.viaversion.viaversion.api.minecraft.HolderSet; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.api.rewriter.ItemRewriter; +import com.viaversion.viaversion.api.type.Types; + +// Base for 1.21.2 and onwards +public class RecipeDisplayRewriter { + protected final Protocol protocol; + + public RecipeDisplayRewriter(final Protocol protocol) { + this.protocol = protocol; + } + + public void registerUpdateRecipes(final C packetType) { + protocol.registerClientbound(packetType, wrapper -> { + final int size = wrapper.passthrough(Types.VAR_INT); + for (int i = 0; i < size; i++) { + wrapper.passthrough(Types.STRING); // Recipe group + rewriteItemIds(wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE)); + } + + final int stonecutterRecipesSize = wrapper.passthrough(Types.VAR_INT); + for (int i = 0; i < stonecutterRecipesSize; i++) { + handleIngredient(wrapper); + handleSlotDisplay(wrapper); + } + }); + } + + public void registerRecipeBookAdd(final C packetType) { + protocol.registerClientbound(packetType, wrapper -> { + final int size = wrapper.passthrough(Types.VAR_INT); + for (int i = 0; i < size; i++) { + wrapper.passthrough(Types.VAR_INT); // Display ID + handleRecipeDisplay(wrapper); + wrapper.passthrough(Types.OPTIONAL_VAR_INT); // Group + wrapper.passthrough(Types.VAR_INT); // Category + if (wrapper.passthrough(Types.BOOLEAN)) { + final int ingredientsSize = wrapper.passthrough(Types.VAR_INT); + for (int j = 0; j < ingredientsSize; j++) { + handleIngredient(wrapper); // Items + } + } + wrapper.passthrough(Types.BYTE); // Flags + } + }); + } + + public void registerPlaceGhostRecipe(final C packetType) { + protocol.registerClientbound(packetType, wrapper -> { + wrapper.passthrough(Types.VAR_INT); // Container ID + handleRecipeDisplay(wrapper); + }); + } + + protected void handleShapeless(final PacketWrapper wrapper) { + handleSlotDisplayList(wrapper); // Ingredients + handleSlotDisplay(wrapper); // Result + handleSlotDisplay(wrapper); // Crafting station + } + + protected void handleShaped(final PacketWrapper wrapper) { + wrapper.passthrough(Types.VAR_INT); // Width + wrapper.passthrough(Types.VAR_INT); // Height + handleSlotDisplayList(wrapper); // Ingredients + handleSlotDisplay(wrapper); // Result + handleSlotDisplay(wrapper); // Crafting station + } + + protected void handleFurnace(final PacketWrapper wrapper) { + handleSlotDisplay(wrapper); // Ingredient + handleSlotDisplay(wrapper); // Fuel + handleSlotDisplay(wrapper); // Result + handleSlotDisplay(wrapper); // Crafting station + } + + protected void handleStoneCutter(final PacketWrapper wrapper) { + handleSlotDisplay(wrapper); // Result + handleSlotDisplay(wrapper); // Crafting station + } + + protected void handleSmithing(final PacketWrapper wrapper) { + handleSlotDisplay(wrapper); // Result + handleSlotDisplay(wrapper); // Crafting station + } + + protected void handleSlotDisplayList(final PacketWrapper wrapper) { + final int size = wrapper.passthrough(Types.VAR_INT); + for (int i = 0; i < size; i++) { + handleSlotDisplay(wrapper); + } + } + + protected void handleRecipeDisplay(final PacketWrapper wrapper) { + final int type = wrapper.passthrough(Types.VAR_INT); + switch (type) { + case 0 -> handleShapeless(wrapper); + case 1 -> handleShaped(wrapper); + case 2 -> handleFurnace(wrapper); + case 3 -> handleStoneCutter(wrapper); + case 4 -> handleSmithing(wrapper); + } + } + + protected void handleSlotDisplay(final PacketWrapper wrapper) { + // empty, any_fuel, smithing_trim are empty + final int type = wrapper.passthrough(Types.VAR_INT); + switch (type) { + case 2 -> handleItemId(wrapper); // Item type + case 3 -> handleItem(wrapper); // Item + case 4 -> wrapper.passthrough(Types.STRING); // Tag key + case 6 -> handleSlotDisplayList(wrapper); // Composite + } + } + + protected void handleIngredient(final PacketWrapper wrapper) { + final HolderSet items = wrapper.passthrough(Types.HOLDER_SET); + if (items.hasTagKey()) { + return; + } + + final int[] ids = items.ids(); + for (int i = 0; i < ids.length; i++) { + ids[i] = rewriteItemId(ids[i]); + } + } + + protected void handleItemId(final PacketWrapper wrapper) { + final int id = wrapper.passthrough(Types.VAR_INT); + wrapper.write(Types.VAR_INT, rewriteItemId(id)); + } + + protected void handleItem(final PacketWrapper wrapper) { + final ItemRewriter itemRewriter = protocol.getItemRewriter(); + final Item item = wrapper.read(itemRewriter.itemType()); + itemRewriter.handleItemToClient(wrapper.user(), item); + wrapper.write(itemRewriter.mappedItemType(), item); + } + + protected int rewriteItemId(final int id) { + if (protocol.getMappingData() != null && protocol.getMappingData().getItemMappings() != null) { + return protocol.getMappingData().getItemMappings().getNewIdOrDefault(id, id); + } + return id; + } + + protected void rewriteItemIds(final int[] ids) { + for (int i = 0; i < ids.length; i++) { + final int id = ids[i]; + ids[i] = rewriteItemId(id); + } + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java index 8704036fe..6985c3e9c 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; +// Base up to and including 1.21.1 public class RecipeRewriter { protected final Protocol protocol; @@ -54,9 +55,6 @@ public class RecipeRewriter { recipeHandlers.put("smithing_transform", this::handleSmithingTransform); recipeHandlers.put("smithing_trim", this::handleSmithingTrim); recipeHandlers.put("crafting_decorated_pot", this::handleSimpleRecipe); - - // Added in 1.21.2 - recipeHandlers.put("crafting_transmute", this::handleTransmute); } public void handleRecipeType(PacketWrapper wrapper, String type) { @@ -163,15 +161,6 @@ public class RecipeRewriter { handleIngredient(wrapper); // Additions } - private void handleTransmute(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - handleIngredient(wrapper); // Input - handleIngredient(wrapper); // Material - final int resultItemId = wrapper.read(Types.VAR_INT); - wrapper.write(Types.VAR_INT, rewrite(resultItemId)); - } - protected int rewrite(final int itemId) { if (protocol.getMappingData() != null && protocol.getItemRewriter() != null) { return protocol.getMappingData().getNewItemId(itemId);