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.
This commit is contained in:
Camotoy 2022-02-21 16:11:51 -05:00
parent 252348ebd8
commit 811ae178c9
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
12 changed files with 184 additions and 280 deletions

View File

@ -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

View File

@ -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<ProtocolPathEntry> 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<Recipe> recipeIterator = Bukkit.getServer().recipeIterator();
while (recipeIterator.hasNext()) {
Recipe recipe = recipeIterator.next();
Pair<ItemStack, ItemData> 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<ItemStack, ItemData> 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<ItemStack, ItemData> 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<ItemStack, ItemData> 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);
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Int2ObjectMap<Recipe>> RECIPES = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
public static final VersionedRegistry<Int2ObjectMap<GeyserRecipe>> 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}

View File

@ -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<RecipeType, List<CraftingData>> craftingData = new EnumMap<>(RecipeType.class);
Int2ObjectMap<Recipe> recipes = new Int2ObjectOpenHashMap<>();
Int2ObjectMap<GeyserRecipe> 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<Recipe> recipes, ItemMappings mappings) {
private static CraftingData getCraftingDataFromJsonNode(JsonNode node, Int2ObjectMap<GeyserRecipe> 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 */

View File

@ -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<Recipe> craftingRecipes;
private Int2ObjectMap<GeyserRecipe> craftingRecipes;
private final Set<String> unlockedRecipes;
private final AtomicInteger lastRecipeNetId;

View File

@ -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;

View File

@ -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<ClientboundUpd
boolean applySmithingRecipes = session.getUpstream().getProtocolVersion() >= Bedrock_v486.V486_CODEC.getProtocolVersion();
Int2ObjectMap<Recipe> recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion()));
Int2ObjectMap<GeyserRecipe> recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion()));
Int2ObjectMap<List<StoneCuttingRecipeData>> unsortedStonecutterData = new Int2ObjectOpenHashMap<>();
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
craftingDataPacket.setCleanRecipes(true);
@ -100,7 +103,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
UUID uuid = UUID.randomUUID();
craftingDataPacket.getCraftingData().add(CraftingData.fromShapeless(uuid.toString(),
Arrays.asList(inputs), Collections.singletonList(output), uuid, "crafting_table", 0, netId));
recipeMap.put(netId++, recipe);
recipeMap.put(netId++, new GeyserShapelessRecipe(shapelessRecipeData));
}
}
case CRAFTING_SHAPED -> {
@ -118,7 +121,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
craftingDataPacket.getCraftingData().add(CraftingData.fromShaped(uuid.toString(),
shapedRecipeData.getWidth(), shapedRecipeData.getHeight(), Arrays.asList(inputs),
Collections.singletonList(output), uuid, "crafting_table", 0, netId));
recipeMap.put(netId++, recipe);
recipeMap.put(netId++, new GeyserShapedRecipe(shapedRecipeData));
}
}
case STONECUTTING -> {

View File

@ -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<Clientbound
}
}
ShapedRecipeData data = new ShapedRecipeData(width, height, "", javaIngredients, item);
// Cache this recipe so we know the client has received it
session.getCraftingRecipes().put(newRecipeId, new Recipe(RecipeType.CRAFTING_SHAPED, uuid.toString(), data));
session.getCraftingRecipes().put(newRecipeId, new GeyserShapedRecipe(width, height, javaIngredients, item));
CraftingDataPacket craftPacket = new CraftingDataPacket();
craftPacket.getCraftingData().add(CraftingData.fromShaped(

View File

@ -28,10 +28,6 @@ package org.geysermc.geyser.util;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
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.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
@ -47,14 +43,17 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.click.Click;
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.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import javax.annotation.Nullable;
import java.util.Arrays;
@ -361,7 +360,7 @@ public class InventoryUtils {
* @param output if not null, the recipe has to output this item
*/
@Nullable
public static Recipe getValidRecipe(final GeyserSession session, final @Nullable ItemStack output, final IntFunction<GeyserItemStack> inventoryGetter,
public static GeyserRecipe getValidRecipe(final GeyserSession session, final @Nullable ItemStack output, final IntFunction<GeyserItemStack> 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;
}