diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index ad67da81e..7ea66e0a3 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -60,7 +60,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -84,15 +83,6 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator RECIPE_TAGS = Map.of( - "minecraft:wood", "minecraft:logs", - "minecraft:wooden_slab", "minecraft:wooden_slabs", - "minecraft:planks", "minecraft:planks"); - private static final Key SMITHING_BASE = MinecraftKey.key("smithing_base"); private static final Key SMITHING_TEMPLATE = MinecraftKey.key("smithing_template"); private static final Key SMITHING_ADDITION = MinecraftKey.key("smithing_addition"); @@ -190,282 +180,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator> recipeIDs = session.getJavaToBedrockRecipeIds(); -// recipeIDs.clear(); -// Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(); -// Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); -// CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); -// craftingDataPacket.setCleanRecipes(true); -// -// RecipeContext context = new RecipeContext(session, craftingDataPacket, recipeMap); -// -// for (Recipe recipe : packet.getRecipes()) { -// switch (recipe.getType()) { -// case CRAFTING_SHAPELESS -> { -// ShapelessRecipeData shapelessRecipeData = (ShapelessRecipeData) recipe.getData(); -// List bedrockRecipeIDs = context.translateShapelessRecipe(new GeyserShapelessRecipe(shapelessRecipeData)); -// if (bedrockRecipeIDs != null) { -// context.addRecipeIdentifier(session, recipe.getIdentifier().asString(), bedrockRecipeIDs); -// } -// } -// case CRAFTING_SHAPED -> { -// ShapedRecipeData shapedRecipeData = (ShapedRecipeData) recipe.getData(); -// List bedrockRecipeIDs = context.translateShapedRecipe(new GeyserShapedRecipe(shapedRecipeData)); -// if (bedrockRecipeIDs != null) { -// context.addRecipeIdentifier(session, recipe.getIdentifier().asString(), bedrockRecipeIDs); -// } -// } -// case STONECUTTING -> { -// StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData(); -// if (stoneCuttingData.getIngredient().getOptions().length == 0) { -// if (GeyserImpl.getInstance().getConfig().isDebugMode()) { -// GeyserImpl.getInstance().getLogger().debug("Received broken stone cutter recipe: " + stoneCuttingData + " " + -// recipe.getIdentifier() + " " + Registries.JAVA_ITEMS.get().get(stoneCuttingData.getResult().getId()).javaIdentifier()); -// } -// continue; -// } -// ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; -// List data = unsortedStonecutterData.get(ingredient.getId()); -// if (data == null) { -// data = new ArrayList<>(); -// unsortedStonecutterData.put(ingredient.getId(), data); -// } -// // Save for processing after all recipes have been received -// data.add(stoneCuttingData); -// } -// case SMITHING_TRANSFORM -> { -// SmithingTransformRecipeData data = (SmithingTransformRecipeData) recipe.getData(); -// ItemData output = ItemTranslator.translateToBedrock(session, data.getResult()); -// -// for (ItemStack template : data.getTemplate().getOptions()) { -// ItemDescriptorWithCount bedrockTemplate = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, template)); -// -// for (ItemStack base : data.getBase().getOptions()) { -// ItemDescriptorWithCount bedrockBase = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, base)); -// -// for (ItemStack addition : data.getAddition().getOptions()) { -// ItemDescriptorWithCount bedrockAddition = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, addition)); -// -// String id = recipe.getIdentifier().asString(); -// // Note: vanilla inputs use aux value of Short.MAX_VALUE -// craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData.of(id, -// bedrockTemplate, bedrockBase, bedrockAddition, output, "smithing_table", context.getAndIncrementNetId())); -// -// recipeIDs.put(id, new ArrayList<>(Collections.singletonList(id))); -// } -// } -// } -// } -// case SMITHING_TRIM -> { -// sendTrimRecipes = true; -// // ignored currently - see below -// } -// case CRAFTING_DECORATED_POT -> { -// // Paper 1.20 seems to send only one recipe, which seems to be hardcoded to include all recipes. -// // We can send the equivalent Bedrock MultiRecipe! :) -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("685a742a-c42e-4a4e-88ea-5eb83fc98e5b"), context.getAndIncrementNetId())); -// } -// case CRAFTING_SPECIAL_BOOKCLONING -> { -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), context.getAndIncrementNetId())); -// } -// case CRAFTING_SPECIAL_REPAIRITEM -> { -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000001"), context.getAndIncrementNetId())); -// } -// case CRAFTING_SPECIAL_MAPEXTENDING -> { -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), context.getAndIncrementNetId())); -// } -// case CRAFTING_SPECIAL_MAPCLONING -> { -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), context.getAndIncrementNetId())); -// } -// case CRAFTING_SPECIAL_FIREWORK_ROCKET -> { -// craftingDataPacket.getCraftingData().add(MultiRecipeData.of(UUID.fromString("00000000-0000-0000-0000-000000000002"), context.getAndIncrementNetId())); -// } -// default -> { -// List recipes = Registries.RECIPES.get(recipe.getType()); -// if (recipes != null) { -// List bedrockRecipeIds = new ArrayList<>(); -// if (recipe.getType() == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) { -// // Only shaped recipe at this moment -// for (GeyserRecipe builtInRecipe : recipes) { -// var recipeIds = context.translateShapedRecipe((GeyserShapedRecipe) builtInRecipe); -// if (recipeIds != null) { -// bedrockRecipeIds.addAll(recipeIds); -// } -// } -// } else if (recipe.getType() == RecipeType.CRAFTING_SPECIAL_SHULKERBOXCOLORING) { -// for (GeyserRecipe builtInRecipe : recipes) { -// var recipeIds = context.translateShulkerBoxRecipe((GeyserShapelessRecipe) builtInRecipe); -// if (recipeIds != null) { -// bedrockRecipeIds.addAll(recipeIds); -// } -// } -// } else { -// for (GeyserRecipe builtInRecipe : recipes) { -// var recipeIds = context.translateShapelessRecipe((GeyserShapelessRecipe) builtInRecipe); -// if (recipeIds != null) { -// bedrockRecipeIds.addAll(recipeIds); -// } -// } -// } -// context.addSpecialRecipesIdentifiers(recipe, bedrockRecipeIds); -// } -// } -// } -// } -// craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES); -// craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion())); -// -// Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); -// for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { -// // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore -// // We can get the correct order for button pressing -// data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData -> -// Registries.JAVA_ITEMS.get().get(stoneCuttingRecipeData.getResult().getId()) -// // See RecipeManager#getRecipesFor as of 1.21 -// .translationKey()))); -// -// // Now that it's sorted, let's translate these recipes -// int buttonId = 0; -// for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) { -// // As of 1.16.4, all stonecutter recipes have one ingredient option -// ItemStack ingredient = stoneCuttingData.getIngredient().getOptions()[0]; -// ItemData input = ItemTranslator.translateToBedrock(session, ingredient); -// ItemDescriptorWithCount descriptor = ItemDescriptorWithCount.fromItem(input); -// ItemStack javaOutput = stoneCuttingData.getResult(); -// ItemData output = ItemTranslator.translateToBedrock(session, javaOutput); -// if (!input.isValid() || !output.isValid()) { -// // Probably modded items -// continue; -// } -// UUID uuid = UUID.randomUUID(); -// // We need to register stonecutting recipes, so they show up on Bedrock -// craftingDataPacket.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shapeless(uuid.toString(), -// Collections.singletonList(descriptor), Collections.singletonList(output), uuid, "stonecutter", 0, context.netId, RecipeUnlockingRequirement.INVALID)); -// -// // Save the recipe list for reference when crafting -// // Add the net ID as the key and the button required + output for the value -// stonecutterRecipeMap.put(context.getAndIncrementNetId(), new GeyserStonecutterData(buttonId++, javaOutput)); -// -// // Currently, stone cutter recipes are not locked/unlocked on Bedrock; so no need to cache their identifiers. -// } -// } -// -// session.getLastRecipeNetId().set(context.netId); // No increment -// -// // Only send smithing trim recipes if Java/ViaVersion sends them. -// if (sendTrimRecipes) { -// // BDS sends armor trim templates and materials before the CraftingDataPacket -// TrimDataPacket trimDataPacket = new TrimDataPacket(); -// trimDataPacket.getPatterns().addAll(session.getRegistryCache().trimPatterns().values()); -// trimDataPacket.getMaterials().addAll(session.getRegistryCache().trimMaterials().values()); -// session.sendUpstreamPacket(trimDataPacket); -// -// // Identical smithing_trim recipe sent by BDS that uses tag-descriptors, as the client seems to ignore the -// // approach of using many default-descriptors (which we do for smithing_transform) -// craftingDataPacket.getCraftingData().add(SmithingTrimRecipeData.of(TrimRecipe.ID, -// TrimRecipe.BASE, TrimRecipe.ADDITION, TrimRecipe.TEMPLATE, "smithing_table", session.getLastRecipeNetId().getAndIncrement())); -// } else { -// // manually add recipes for the upgrade template (workaround), since Java pre-1.20 doesn't -// craftingDataPacket.getCraftingData().addAll(getSmithingTransformRecipes(session)); -// } -// session.setOldSmithingTable(!sendTrimRecipes); -// session.sendUpstreamPacket(craftingDataPacket); -// session.setCraftingRecipes(recipeMap); -// session.setStonecutterRecipes(stonecutterRecipeMap); -// } -// -// //TODO: rewrite -// /** -// * The Java server sends an array of items for each ingredient you can use per slot in the crafting grid. -// * Bedrock recipes take only one ingredient per crafting grid slot. -// * -// * @return the Java ingredient list as an array that Bedrock can understand -// */ -// private static ItemDescriptorWithCount[][] combinations(GeyserSession session, Ingredient[] ingredients) { -// boolean empty = true; -// Map, IntSet> squashedOptions = new HashMap<>(); -// for (int i = 0; i < ingredients.length; i++) { -// if (ingredients[i].getOptions().length == 0) { -// squashedOptions.computeIfAbsent(Collections.singleton(ItemDescriptorWithCount.EMPTY), k -> new IntOpenHashSet()).add(i); -// continue; -// } -// empty = false; -// Ingredient ingredient = ingredients[i]; -// Map> groupedByIds = Arrays.stream(ingredient.getOptions()) -// .map(item -> ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, item))) -// .collect(Collectors.groupingBy(item -> item == ItemDescriptorWithCount.EMPTY ? new GroupedItem(ItemDefinition.AIR, 0) : new GroupedItem(((DefaultDescriptor) item.getDescriptor()).getItemId(), item.getCount()))); -// Set optionSet = new HashSet<>(groupedByIds.size()); -// for (Map.Entry> entry : groupedByIds.entrySet()) { -// if (entry.getValue().size() > 1) { -// GroupedItem groupedItem = entry.getKey(); -// -// String recipeTag = RECIPE_TAGS.get(groupedItem.id.getIdentifier()); -// if (recipeTag != null && ingredients.length > 1) { -// optionSet.add(new ItemDescriptorWithCount(new ItemTagDescriptor(recipeTag), groupedItem.count)); -// continue; -// } -// -// int idCount = 0; -// //not optimal -// for (ItemMapping mapping : session.getItemMappings().getItems()) { -// if (mapping.getBedrockDefinition() == groupedItem.id) { -// idCount++; -// } -// } -// if (entry.getValue().size() < idCount) { -// optionSet.addAll(entry.getValue()); -// } else { -// optionSet.add(groupedItem.id == ItemDefinition.AIR ? ItemDescriptorWithCount.EMPTY : new ItemDescriptorWithCount(new DefaultDescriptor(groupedItem.id, Short.MAX_VALUE), groupedItem.count)); -// } -// } else { -// ItemDescriptorWithCount item = entry.getValue().get(0); -// optionSet.add(item); -// } -// } -// squashedOptions.computeIfAbsent(optionSet, k -> new IntOpenHashSet()).add(i); -// } -// if (empty) { -// // Crashes Bedrock 1.19.70 otherwise -// // Fixes https://github.com/GeyserMC/Geyser/issues/3549 -// return null; -// } -// int totalCombinations = 1; -// for (Set optionSet : squashedOptions.keySet()) { -// totalCombinations *= optionSet.size(); -// } -// if (totalCombinations > 500) { -// ItemDescriptorWithCount[] translatedItems = new ItemDescriptorWithCount[ingredients.length]; -// for (int i = 0; i < ingredients.length; i++) { -// if (ingredients[i].getOptions().length > 0) { -// translatedItems[i] = ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, ingredients[i].getOptions()[0])); -// } else { -// translatedItems[i] = ItemDescriptorWithCount.EMPTY; -// } -// } -// return new ItemDescriptorWithCount[][]{translatedItems}; -// } -// List> sortedSets = new ArrayList<>(squashedOptions.keySet()); -// sortedSets.sort(Comparator.comparing(Set::size, Comparator.reverseOrder())); -// ItemDescriptorWithCount[][] combinations = new ItemDescriptorWithCount[totalCombinations][ingredients.length]; -// int x = 1; -// for (Set set : sortedSets) { -// IntSet slotSet = squashedOptions.get(set); -// int i = 0; -// for (ItemDescriptorWithCount item : set) { -// for (int j = 0; j < totalCombinations / set.size(); j++) { -// final int comboIndex = (i * x) + (j % x) + ((j / x) * set.size() * x); -// for (IntIterator it = slotSet.iterator(); it.hasNext(); ) { -// combinations[comboIndex][it.nextInt()] = item; -// } -// } -// i++; -// } -// x *= set.size(); -// } -// return combinations; -// } -// + private void addSmithingTransformRecipes(GeyserSession session, List recipes) { ItemMapping template = session.getItemMappings().getStoredItems().upgradeTemplate(); @@ -488,135 +203,4 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator recipeMap; -// // Get the last known network ID (first used for some pregenerated recipes) and increment from there. -// private int netId = InventoryUtils.LAST_RECIPE_NET_ID + 1; -// -// private RecipeContext(GeyserSession session, CraftingDataPacket packet, Int2ObjectMap recipeMap) { -// this.session = session; -// this.packet = packet; -// this.recipeMap = recipeMap; -// } -// -// List translateShulkerBoxRecipe(GeyserShapelessRecipe recipe) { -// ItemStack result = recipe.result(); -// ItemData output = ItemTranslator.translateToBedrock(session, result); -// if (!output.isValid()) { -// // Likely modded item that Bedrock will complain about if it persists -// return null; -// } -// -// Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); -// if (!(javaItem instanceof BedrockRequiresTagItem)) { -// // Strip NBT - tools won't appear in the recipe book otherwise -// output = output.toBuilder().tag(null).build(); -// } -// ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); -// if (inputCombinations == null) { -// return null; -// } -// -// List bedrockRecipeIDs = new ArrayList<>(); -// for (ItemDescriptorWithCount[] inputs : inputCombinations) { -// UUID uuid = UUID.randomUUID(); -// bedrockRecipeIDs.add(uuid.toString()); -// packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shulkerBox(uuid.toString(), -// Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId)); -// recipeMap.put(netId++, recipe); -// } -// return bedrockRecipeIDs; -// } -// -// List translateShapelessRecipe(GeyserShapelessRecipe recipe) { -// ItemStack result = recipe.result(); -// ItemData output = ItemTranslator.translateToBedrock(session, result); -// if (!output.isValid()) { -// // Likely modded item that Bedrock will complain about if it persists -// return null; -// } -// -// Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); -// if (!(javaItem instanceof BedrockRequiresTagItem)) { -// // Strip NBT - tools won't appear in the recipe book otherwise -// output = output.toBuilder().tag(null).build(); -// } -// ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); -// if (inputCombinations == null) { -// return null; -// } -// -// List bedrockRecipeIDs = new ArrayList<>(); -// for (ItemDescriptorWithCount[] inputs : inputCombinations) { -// UUID uuid = UUID.randomUUID(); -// bedrockRecipeIDs.add(uuid.toString()); -// packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData.shapeless(uuid.toString(), -// Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId, RecipeUnlockingRequirement.INVALID)); -// recipeMap.put(netId++, recipe); -// } -// return bedrockRecipeIDs; -// } -// -// List translateShapedRecipe(GeyserShapedRecipe recipe) { -// ItemStack result = recipe.result(); -// ItemData output = ItemTranslator.translateToBedrock(session, result); -// if (!output.isValid()) { -// // Likely modded item that Bedrock will complain about if it persists -// return null; -// } -// -// Item javaItem = Registries.JAVA_ITEMS.get(result.getId()); -// if (!(javaItem instanceof BedrockRequiresTagItem)) { -// // Strip NBT - tools won't appear in the recipe book otherwise -// output = output.toBuilder().tag(null).build(); -// } -// ItemDescriptorWithCount[][] inputCombinations = combinations(session, recipe.ingredients()); -// if (inputCombinations == null) { -// return null; -// } -// -// List bedrockRecipeIDs = new ArrayList<>(); -// for (ItemDescriptorWithCount[] inputs : inputCombinations) { -// UUID uuid = UUID.randomUUID(); -// bedrockRecipeIDs.add(uuid.toString()); -// packet.getCraftingData().add(org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData.shaped(uuid.toString(), -// recipe.width(), recipe.height(), Arrays.asList(inputs), -// Collections.singletonList(output), uuid, "crafting_table", 0, netId, false, RecipeUnlockingRequirement.INVALID)); -// recipeMap.put(netId++, recipe); -// } -// return bedrockRecipeIDs; -// } -// -// void addSpecialRecipesIdentifiers(Recipe recipe, List identifiers) { -// String javaRecipeID = switch (recipe.getType()) { -// case CRAFTING_SPECIAL_SHULKERBOXCOLORING -> -// // BDS (un)locks the dyeing with the shulker box recipe, Java never - we want BDS behavior for ease of use -// "minecraft:shulker_box"; -// case CRAFTING_SPECIAL_TIPPEDARROW -> -// // similar as above -// "minecraft:arrow"; -// default -> recipe.getIdentifier().asString(); -// }; -// -// addRecipeIdentifier(session, javaRecipeID, identifiers); -// } -// -// void addRecipeIdentifier(GeyserSession session, String javaIdentifier, List bedrockIdentifiers) { -// session.getJavaToBedrockRecipeIds().computeIfAbsent(javaIdentifier, k -> new ArrayList<>()).addAll(bedrockIdentifiers); -// } -// -// int getAndIncrementNetId() { -// return this.netId++; -// } -// } }