From 811ae178c97bd6b6b9505516796ebb3bff368009 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 21 Feb 2022 16:11:51 -0500 Subject: [PATCH] Store recipes in a more compact GeyserRecipe type This prevents us from storing some unnecessary data. Also removes some 1.11 recipe compatibility code that is essentially unusable. --- .../platform/spigot/GeyserSpigotPlugin.java | 20 +- .../GeyserSpigot1_11CraftingListener.java | 203 ------------------ .../geyser/inventory/recipe/GeyserRecipe.java | 36 ++++ .../inventory/recipe/GeyserShapedRecipe.java | 43 ++++ .../recipe/GeyserShapelessRecipe.java | 42 ++++ .../geysermc/geyser/registry/Registries.java | 3 +- .../populator/RecipeRegistryPopulator.java | 17 +- .../geyser/session/GeyserSession.java | 4 +- .../inventory/InventoryTranslator.java | 40 ++-- .../java/JavaUpdateRecipesTranslator.java | 9 +- .../JavaContainerSetSlotTranslator.java | 7 +- .../geysermc/geyser/util/InventoryUtils.java | 40 ++-- 12 files changed, 184 insertions(+), 280 deletions(-) delete mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java create mode 100644 core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java create mode 100644 core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java create mode 100644 core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index bdf28a203..aae6c599a 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -32,27 +32,26 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.common.PlatformType; -import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.command.CommandManager; -import org.geysermc.geyser.session.auth.AuthType; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; -import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.level.WorldManager; +import org.geysermc.geyser.network.MinecraftProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; -import org.geysermc.geyser.Constants; -import org.geysermc.geyser.util.FileUtils; -import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender; import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener; -import org.geysermc.geyser.platform.spigot.world.GeyserSpigot1_11CraftingListener; import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener; import org.geysermc.geyser.platform.spigot.world.manager.*; +import org.geysermc.geyser.session.auth.AuthType; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; @@ -235,11 +234,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this); - if (isPre1_12) { - // Register events needed to send all recipes to the client - Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigot1_11CraftingListener(geyser), this); - } - this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(geyser)); // Check to ensure the current setup can support the protocol version Geyser uses diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java deleted file mode 100644 index 78a64e47b..000000000 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/world/GeyserSpigot1_11CraftingListener.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.platform.spigot.world; - -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; -import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; -import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; -import com.viaversion.viaversion.api.Via; -import com.viaversion.viaversion.api.data.MappingData; -import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; -import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; -import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; -import com.viaversion.viaversion.util.Pair; -import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.inventory.Recipe; -import org.bukkit.inventory.ShapedRecipe; -import org.bukkit.inventory.ShapelessRecipe; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.network.MinecraftProtocol; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.inventory.item.ItemTranslator; -import org.geysermc.geyser.util.InventoryUtils; - -import java.util.*; - -/** - * Used to send all available recipes from the server to the client, as a valid recipe book packet won't be sent by the server. - * Requires ViaVersion. - */ -public class GeyserSpigot1_11CraftingListener implements Listener { - - private final GeyserImpl geyser; - /** - * Specific mapping data for 1.12 to 1.13. Used to convert the 1.12 item into 1.13. - */ - private final MappingData mappingData1_12to1_13; - /** - * The list of all protocols from the client's version to 1.13. - */ - private final List protocolList; - - public GeyserSpigot1_11CraftingListener(GeyserImpl geyser) { - this.geyser = geyser; - this.mappingData1_12to1_13 = Via.getManager().getProtocolManager().getProtocol(Protocol1_13To1_12_2.class).getMappingData(); - this.protocolList = Via.getManager().getProtocolManager().getProtocolPath(MinecraftProtocol.getJavaProtocolVersion(), - ProtocolVersion.v1_13.getVersion()); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - GeyserSession session = null; - for (GeyserSession otherSession : geyser.getSessionManager().getSessions().values()) { - if (otherSession.name().equals(event.getPlayer().getName())) { - session = otherSession; - break; - } - } - if (session == null) { - return; - } - - sendServerRecipes(session); - } - - public void sendServerRecipes(GeyserSession session) { - int netId = InventoryUtils.LAST_RECIPE_NET_ID; - - CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); - craftingDataPacket.setCleanRecipes(true); - - Iterator recipeIterator = Bukkit.getServer().recipeIterator(); - while (recipeIterator.hasNext()) { - Recipe recipe = recipeIterator.next(); - - Pair outputs = translateToBedrock(session, recipe.getResult()); - ItemStack javaOutput = outputs.getKey(); - ItemData output = outputs.getValue(); - if (output == null || output.getId() == 0) continue; // If items make air we don't want that - - boolean isNotAllAir = false; // Check for all-air recipes - if (recipe instanceof ShapedRecipe shapedRecipe) { - int size = shapedRecipe.getShape().length * shapedRecipe.getShape()[0].length(); - Ingredient[] ingredients = new Ingredient[size]; - ItemData[] input = new ItemData[size]; - for (int i = 0; i < input.length; i++) { - // Index is converting char to integer, adding i then converting back to char based on ASCII code - Pair result = translateToBedrock(session, shapedRecipe.getIngredientMap().get((char) ('a' + i))); - ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()}); - input[i] = result.getValue(); - isNotAllAir |= input[i].getId() != 0; - } - - if (!isNotAllAir) continue; - UUID uuid = UUID.randomUUID(); - // Add recipe to our internal cache - ShapedRecipeData data = new ShapedRecipeData(shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length, - "", ingredients, javaOutput); - session.getCraftingRecipes().put(netId, - new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data)); - - // Add recipe for Bedrock - craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(), - shapedRecipe.getShape()[0].length(), shapedRecipe.getShape().length, Arrays.asList(input), - Collections.singletonList(output), uuid, "crafting_table", 0, netId++)); - } else if (recipe instanceof ShapelessRecipe shapelessRecipe) { - Ingredient[] ingredients = new Ingredient[shapelessRecipe.getIngredientList().size()]; - ItemData[] input = new ItemData[shapelessRecipe.getIngredientList().size()]; - - for (int i = 0; i < input.length; i++) { - Pair result = translateToBedrock(session, shapelessRecipe.getIngredientList().get(i)); - ingredients[i] = new Ingredient(new ItemStack[]{result.getKey()}); - input[i] = result.getValue(); - isNotAllAir |= input[i].getId() != 0; - } - - if (!isNotAllAir) continue; - UUID uuid = UUID.randomUUID(); - // Add recipe to our internal cache - ShapelessRecipeData data = new ShapelessRecipeData("", ingredients, javaOutput); - session.getCraftingRecipes().put(netId, - new com.github.steveice10.mc.protocol.data.game.recipe.Recipe(RecipeType.CRAFTING_SHAPELESS, uuid.toString(), data)); - - // Add recipe for Bedrock - craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(), - Arrays.asList(input), Collections.singletonList(output), uuid, "crafting_table", 0, netId++)); - } - } - - session.sendUpstreamPacket(craftingDataPacket); - } - - @SuppressWarnings("deprecation") - private Pair translateToBedrock(GeyserSession session, org.bukkit.inventory.ItemStack itemStack) { - if (itemStack != null && itemStack.getData() != null) { - if (itemStack.getType().getId() == 0) { - return new Pair<>(null, ItemData.AIR); - } - - int legacyId = (itemStack.getType().getId() << 4) | (itemStack.getData().getData() & 0xFFFF); - - if (itemStack.getType().getId() == 355 && itemStack.getData().getData() == (byte) 0) { // Handle bed color since the server will always be pre-1.12 - legacyId = (itemStack.getType().getId() << 4) | ((byte) 14 & 0xFFFF); - } - - // old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 and so on - int itemId; - if (mappingData1_12to1_13.getItemMappings().containsKey(legacyId)) { - itemId = mappingData1_12to1_13.getNewItemId(legacyId); - } else if (mappingData1_12to1_13.getItemMappings().containsKey((itemStack.getType().getId() << 4) | (0))) { - itemId = mappingData1_12to1_13.getNewItemId((itemStack.getType().getId() << 4) | (0)); - } else { - // No ID found, just send back air - return new Pair<>(null, ItemData.AIR); - } - - for (int i = protocolList.size() - 1; i >= 0; i--) { - MappingData mappingData = protocolList.get(i).getProtocol().getMappingData(); - if (mappingData != null) { - itemId = mappingData.getNewItemId(itemId); - } - } - - ItemStack mcItemStack = new ItemStack(itemId, itemStack.getAmount()); - ItemData finalData = ItemTranslator.translateToBedrock(session, mcItemStack); - return new Pair<>(mcItemStack, finalData); - } - - // Empty slot, most likely - return new Pair<>(null, ItemData.AIR); - } - -} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java new file mode 100644 index 000000000..641d5ad94 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserRecipe.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.inventory.recipe; + +/** + * A more compact version of {@link com.github.steveice10.mc.protocol.data.game.recipe.Recipe}. + */ +public interface GeyserRecipe { + /** + * Whether the recipe is flexible or not in which items can be placed where. + */ + boolean isShaped(); +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java new file mode 100644 index 000000000..a011fef6d --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapedRecipe.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.inventory.recipe; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; +import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; + +public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, ItemStack result) implements GeyserRecipe { + + public GeyserShapedRecipe(ShapedRecipeData data) { + this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult()); + } + + @Override + public boolean isShaped() { + return true; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java new file mode 100644 index 000000000..6c7665bbb --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/GeyserShapelessRecipe.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.inventory.recipe; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; +import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; + +public record GeyserShapelessRecipe(Ingredient[] ingredients, ItemStack result) implements GeyserRecipe { + + public GeyserShapelessRecipe(ShapelessRecipeData data) { + this(data.getIngredients(), data.getResult()); + } + + @Override + public boolean isShaped() { + return false; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index 5a60351ce..20e9d3515 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -43,6 +43,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment; +import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.registry.loader.*; import org.geysermc.geyser.registry.populator.ItemRegistryPopulator; import org.geysermc.geyser.registry.populator.PacketRegistryPopulator; @@ -141,7 +142,7 @@ public final class Registries { /** * A versioned registry holding all the recipes, with the net ID being the key, and {@link Recipe} as the value. */ - public static final VersionedRegistry> RECIPES = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + public static final VersionedRegistry> RECIPES = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); /** * A mapped registry holding the available records, with the ID of the record being the key, and the {@link com.nukkitx.protocol.bedrock.data.SoundEvent} diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java index f32aeef51..f0a215f2a 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/RecipeRegistryPopulator.java @@ -28,10 +28,7 @@ package org.geysermc.geyser.registry.populator; import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; -import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; @@ -40,6 +37,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.inventory.recipe.GeyserRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; @@ -71,7 +71,7 @@ public class RecipeRegistryPopulator { // Make a bit of an assumption here that the last recipe net ID will be equivalent between all versions LAST_RECIPE_NET_ID = currentRecipeId; Map> craftingData = new EnumMap<>(RecipeType.class); - Int2ObjectMap recipes = new Int2ObjectOpenHashMap<>(); + Int2ObjectMap recipes = new Int2ObjectOpenHashMap<>(); craftingData.put(RecipeType.CRAFTING_SPECIAL_BOOKCLONING, Collections.singletonList(CraftingData.fromMulti(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), ++LAST_RECIPE_NET_ID))); @@ -124,7 +124,7 @@ public class RecipeRegistryPopulator { * @param recipes a list of all the recipes * @return the {@link CraftingData} to send to the Bedrock client. */ - private static CraftingData getCraftingDataFromJsonNode(JsonNode node, Int2ObjectMap recipes, ItemMappings mappings) { + private static CraftingData getCraftingDataFromJsonNode(JsonNode node, Int2ObjectMap recipes, ItemMappings mappings) { int netId = ++LAST_RECIPE_NET_ID; int type = node.get("bedrockRecipeType").asInt(); JsonNode outputNode = node.get("output"); @@ -165,9 +165,8 @@ public class RecipeRegistryPopulator { for (ItemData input : inputs) { ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input, mappings)})); } - ShapedRecipeData data = new ShapedRecipeData(shape.get(0).length(), shape.size(), "crafting_table", + GeyserRecipe recipe = new GeyserShapedRecipe(shape.get(0).length(), shape.size(), ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output, mappings)); - Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPED, "", data); recipes.put(netId, recipe); /* Convert end */ @@ -185,9 +184,7 @@ public class RecipeRegistryPopulator { for (ItemData input : inputs) { ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input, mappings)})); } - ShapelessRecipeData data = new ShapelessRecipeData("crafting_table", - ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output, mappings)); - Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPELESS, "", data); + GeyserRecipe recipe = new GeyserShapelessRecipe(ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output, mappings)); recipes.put(netId, recipe); /* Convert end */ diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index b886f8b20..c2e6ae6f6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -39,7 +39,6 @@ import com.github.steveice10.mc.protocol.data.UnexpectedEncryptionException; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; -import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility; import com.github.steveice10.mc.protocol.data.game.setting.SkinPart; import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; @@ -94,6 +93,7 @@ import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; +import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.level.physics.CollisionManager; import org.geysermc.geyser.network.netty.LocalSession; @@ -350,7 +350,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { private Entity mouseoverEntity; @Setter - private Int2ObjectMap craftingRecipes; + private Int2ObjectMap craftingRecipes; private final Set unlockedRecipes; private final AtomicInteger lastRecipeNetId; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index e6a9faf74..b48709595 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -28,9 +28,6 @@ package org.geysermc.geyser.translator.inventory; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; -import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; @@ -45,6 +42,9 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.click.ClickPlan; +import org.geysermc.geyser.inventory.recipe.GeyserRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator; import org.geysermc.geyser.translator.inventory.chest.SingleChestInventoryTranslator; @@ -535,7 +535,6 @@ public abstract class InventoryTranslator { } int gridDimensions = gridSize == 4 ? 2 : 3; - Recipe recipe; Ingredient[] ingredients = new Ingredient[0]; ItemStack output = null; int recipeWidth = 0; @@ -564,7 +563,7 @@ public abstract class InventoryTranslator { craftState = CraftState.RECIPE_ID; int recipeId = autoCraftAction.getRecipeNetworkId(); - recipe = session.getCraftingRecipes().get(recipeId); + GeyserRecipe recipe = session.getCraftingRecipes().get(recipeId); if (recipe == null) { return rejectRequest(request); } @@ -578,24 +577,21 @@ public abstract class InventoryTranslator { } } - switch (recipe.getType()) { - case CRAFTING_SHAPED -> { - ShapedRecipeData shapedData = (ShapedRecipeData) recipe.getData(); - ingredients = shapedData.getIngredients(); - recipeWidth = shapedData.getWidth(); - output = shapedData.getResult(); - if (shapedData.getWidth() > gridDimensions || shapedData.getHeight() > gridDimensions) { - return rejectRequest(request); - } + if (recipe.isShaped()) { + GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe; + ingredients = shapedRecipe.ingredients(); + recipeWidth = shapedRecipe.width(); + output = shapedRecipe.result(); + if (recipeWidth > gridDimensions || shapedRecipe.height() > gridDimensions) { + return rejectRequest(request); } - case CRAFTING_SHAPELESS -> { - ShapelessRecipeData shapelessData = (ShapelessRecipeData) recipe.getData(); - ingredients = shapelessData.getIngredients(); - recipeWidth = gridDimensions; - output = shapelessData.getResult(); - if (ingredients.length > gridSize) { - return rejectRequest(request); - } + } else { + GeyserShapelessRecipe shapelessRecipe = (GeyserShapelessRecipe) recipe; + ingredients = shapelessRecipe.ingredients(); + recipeWidth = gridDimensions; + output = shapelessRecipe.result(); + if (ingredients.length > gridSize) { + return rejectRequest(request); } } break; 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 c3c8abfb4..4d7a1617a 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 @@ -42,6 +42,9 @@ import com.nukkitx.protocol.bedrock.v486.Bedrock_v486; import it.unimi.dsi.fastutil.ints.*; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import org.geysermc.geyser.inventory.recipe.GeyserRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; +import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -80,7 +83,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator= Bedrock_v486.V486_CODEC.getProtocolVersion(); - Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion())); + Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion())); Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); @@ -100,7 +103,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator { @@ -118,7 +121,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java index 4bb2a8e60..36307e7bd 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/inventory/JavaContainerSetSlotTranslator.java @@ -27,9 +27,6 @@ package org.geysermc.geyser.translator.protocol.java.inventory; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient; -import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; -import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; @@ -38,6 +35,7 @@ import com.nukkitx.protocol.bedrock.packet.CraftingDataPacket; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator; @@ -165,9 +163,8 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator inventoryGetter, + public static GeyserRecipe getValidRecipe(final GeyserSession session, final @Nullable ItemStack output, final IntFunction inventoryGetter, final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) { int nonAirCount = 0; // Used for shapeless recipes for amount of items needed in recipe for (int row = firstRow; row < height + firstRow; row++) { @@ -373,14 +372,14 @@ public class InventoryUtils { } recipes: - for (Recipe recipe : session.getCraftingRecipes().values()) { - if (recipe.getType() == RecipeType.CRAFTING_SHAPED) { - ShapedRecipeData data = (ShapedRecipeData) recipe.getData(); - if (output != null && !data.getResult().equals(output)) { + for (GeyserRecipe recipe : session.getCraftingRecipes().values()) { + if (recipe.isShaped()) { + GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe; + if (output != null && !shapedRecipe.result().equals(output)) { continue; } - Ingredient[] ingredients = data.getIngredients(); - if (data.getWidth() != width || data.getHeight() != height || width * height != ingredients.length) { + Ingredient[] ingredients = shapedRecipe.ingredients(); + if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.length) { continue; } @@ -397,18 +396,17 @@ public class InventoryUtils { continue; } } - return recipe; - } else if (recipe.getType() == RecipeType.CRAFTING_SHAPELESS) { - ShapelessRecipeData data = (ShapelessRecipeData) recipe.getData(); - if (output != null && !data.getResult().equals(output)) { + } else { + GeyserShapelessRecipe data = (GeyserShapelessRecipe) recipe; + if (output != null && !data.result().equals(output)) { continue; } - if (nonAirCount != data.getIngredients().length) { + if (nonAirCount != data.ingredients().length) { // There is an amount of items on the crafting table that is not the same as the ingredient count so this is invalid continue; } - for (int i = 0; i < data.getIngredients().length; i++) { - Ingredient ingredient = data.getIngredients()[i]; + for (int i = 0; i < data.ingredients().length; i++) { + Ingredient ingredient = data.ingredients()[i]; for (ItemStack itemStack : ingredient.getOptions()) { boolean inventoryHasItem = false; // Iterate only over the crafting table to find this item @@ -432,8 +430,8 @@ public class InventoryUtils { } } } - return recipe; } + return recipe; } return null; }