3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-26 00:00:41 +01:00

Re-implement on-the-fly recipe sending

Dieser Commit ist enthalten in:
Camotoy 2024-10-28 18:53:16 -04:00
Ursprung 344d40dd1f
Commit 05f153c941
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
11 geänderte Dateien mit 170 neuen und 98 gelöschten Zeilen

Datei anzeigen

@ -29,6 +29,7 @@ import lombok.*;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
@ -38,6 +39,10 @@ import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import java.util.HashMap; import java.util.HashMap;
@ -77,6 +82,20 @@ public class GeyserItemStack {
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents()); return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents());
} }
public static @NonNull GeyserItemStack from(@NonNull SlotDisplay slotDisplay) {
if (slotDisplay instanceof EmptySlotDisplay) {
return GeyserItemStack.EMPTY;
}
if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) {
return GeyserItemStack.of(itemSlotDisplay.item(), 1);
}
if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) {
return GeyserItemStack.from(itemStackSlotDisplay.itemStack());
}
GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay);
return GeyserItemStack.EMPTY;
}
public int getJavaId() { public int getJavaId() {
return isEmpty() ? 0 : javaId; return isEmpty() ? 0 : javaId;
} }

Datei anzeigen

@ -26,7 +26,7 @@
package org.geysermc.geyser.inventory.recipe; package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
/** /**
* A more compact version of {link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}. * A more compact version of {link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}.
@ -38,5 +38,5 @@ public interface GeyserRecipe {
boolean isShaped(); boolean isShaped();
@Nullable @Nullable
ItemStack result(); SlotDisplay result();
} }

Datei anzeigen

@ -26,14 +26,16 @@
package org.geysermc.geyser.inventory.recipe; package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapedCraftingRecipeDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe { import java.util.List;
// public GeyserShapedRecipe(ShapedRecipeData data) { public record GeyserShapedRecipe(int width, int height, List<SlotDisplay> ingredients, @Nullable SlotDisplay result) implements GeyserRecipe {
// this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult());
// } public GeyserShapedRecipe(ShapedCraftingRecipeDisplay data) {
this(data.width(), data.height(), data.ingredients(), data.result());
}
@Override @Override
public boolean isShaped() { public boolean isShaped() {

Datei anzeigen

@ -26,14 +26,16 @@
package org.geysermc.geyser.inventory.recipe; package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapelessCraftingRecipeDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
public record GeyserShapelessRecipe(Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe { import java.util.List;
// public GeyserShapelessRecipe(ShapelessRecipeData data) { public record GeyserShapelessRecipe(List<SlotDisplay> ingredients, @Nullable SlotDisplay result) implements GeyserRecipe {
// this(data.getIngredients(), data.getResult());
// } public GeyserShapelessRecipe(ShapelessCraftingRecipeDisplay data) {
this(data.ingredients(), data.result());
}
@Override @Override
public boolean isShaped() { public boolean isShaped() {

Datei anzeigen

@ -31,8 +31,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper; import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -92,7 +90,7 @@ public abstract class RecipeRegistryLoader implements RegistryLoader<String, Map
for (int i = 0; i < rawInputs.size(); i++) { for (int i = 0; i < rawInputs.size(); i++) {
//javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)}); //javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)});
} }
deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output)); //deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output));
} }
return deserializedRecipes; return deserializedRecipes;
} }
@ -118,7 +116,7 @@ public abstract class RecipeRegistryLoader implements RegistryLoader<String, Map
//inputs[i++] = new Ingredient(new ItemStack[] {stack}); //inputs[i++] = new Ingredient(new ItemStack[] {stack});
} }
} }
deserializedRecipes.add(new GeyserShapedRecipe(shape.size(), shape.get(0).length, inputs, output)); //deserializedRecipes.add(new GeyserShapedRecipe(shape.size(), shape.get(0).length, inputs, output));
} }
return deserializedRecipes; return deserializedRecipes;
} }

Datei anzeigen

@ -446,8 +446,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
*/ */
private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds; private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds;
@Setter private final Int2ObjectMap<GeyserRecipe> craftingRecipes;
private Int2ObjectMap<GeyserRecipe> craftingRecipes;
private final AtomicInteger lastRecipeNetId; private final AtomicInteger lastRecipeNetId;
/** /**

Datei anzeigen

@ -25,21 +25,40 @@
package org.geysermc.geyser.translator.inventory; package org.geysermc.geyser.translator.inventory;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.*; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.AutoCraftRecipeAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ConsumeAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.CraftResultsDeprecatedAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DropAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseContainer; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseContainer;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseSlot; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseSlot;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseStatus; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseStatus;
import org.cloudburstmc.protocol.bedrock.packet.ItemStackResponsePacket; import org.cloudburstmc.protocol.bedrock.packet.ItemStackResponsePacket;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.*; import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.CartographyContainer;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.click.Click; import org.geysermc.geyser.inventory.click.Click;
import org.geysermc.geyser.inventory.click.ClickPlan; import org.geysermc.geyser.inventory.click.ClickPlan;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe; import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
@ -56,12 +75,17 @@ import org.geysermc.geyser.translator.inventory.furnace.SmokerInventoryTranslato
import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.ItemUtils; import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AllArgsConstructor @AllArgsConstructor
public abstract class InventoryTranslator { public abstract class InventoryTranslator {
@ -642,8 +666,8 @@ public abstract class InventoryTranslator {
} }
int gridDimensions = gridSize == 4 ? 2 : 3; int gridDimensions = gridSize == 4 ? 2 : 3;
Ingredient[] ingredients = new Ingredient[0]; List<SlotDisplay> ingredients = Collections.emptyList();
ItemStack output = null; SlotDisplay output = null;
int recipeWidth = 0; int recipeWidth = 0;
int ingRemaining = 0; int ingRemaining = 0;
int ingredientIndex = -1; int ingredientIndex = -1;
@ -697,7 +721,7 @@ public abstract class InventoryTranslator {
ingredients = shapelessRecipe.ingredients(); ingredients = shapelessRecipe.ingredients();
recipeWidth = gridDimensions; recipeWidth = gridDimensions;
output = shapelessRecipe.result(); output = shapelessRecipe.result();
if (ingredients.length > gridSize) { if (ingredients.size() > gridSize) {
return rejectRequest(request); return rejectRequest(request);
} }
} }
@ -728,11 +752,11 @@ public abstract class InventoryTranslator {
craftState = CraftState.INGREDIENTS; craftState = CraftState.INGREDIENTS;
if (ingRemaining == 0) { if (ingRemaining == 0) {
while (++ingredientIndex < ingredients.length) { while (++ingredientIndex < ingredients.size()) {
// if (ingredients[ingredientIndex].getOptions().length != 0) { if (!(ingredients.get(ingredientIndex) instanceof EmptySlotDisplay)) { // TODO I guess can technically other options be empty?
// ingRemaining = timesCrafted; ingRemaining = timesCrafted;
// break; break;
// } }
} }
} }

Datei anzeigen

@ -42,7 +42,7 @@ import java.util.UUID;
import static org.geysermc.geyser.util.InventoryUtils.LAST_RECIPE_NET_ID; import static org.geysermc.geyser.util.InventoryUtils.LAST_RECIPE_NET_ID;
@Translator(packet = ClientboundFinishConfigurationPacket.class) @Translator(packet = ClientboundFinishConfigurationPacket.class)
public class JavaFinishConfigurationPacketTranslator extends PacketTranslator<ClientboundFinishConfigurationPacket> { public class JavaFinishConfigurationTranslator extends PacketTranslator<ClientboundFinishConfigurationPacket> {
/** /**
* Required to use the specified cartography table recipes * Required to use the specified cartography table recipes
*/ */
@ -72,6 +72,9 @@ public class JavaFinishConfigurationPacketTranslator extends PacketTranslator<Cl
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion())); craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(session.getUpstream().getProtocolVersion()));
if (session.isSentSpawnPacket()) { if (session.isSentSpawnPacket()) {
session.getUpstream().sendPacket(craftingDataPacket); session.getUpstream().sendPacket(craftingDataPacket);
session.getCraftingRecipes().clear();
session.getJavaToBedrockRecipeIds().clear();
session.getStonecutterRecipes().clear();
} else { } else {
session.getUpstream().queuePostStartGamePacket(craftingDataPacket); session.getUpstream().queuePostStartGamePacket(craftingDataPacket);
} }

Datei anzeigen

@ -39,6 +39,9 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.DefaultDescri
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket; import org.cloudburstmc.protocol.bedrock.packet.UnlockedRecipesPacket;
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.item.Items; import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.BedrockRequiresTagItem; import org.geysermc.geyser.item.type.BedrockRequiresTagItem;
import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.type.Item;
@ -79,9 +82,10 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
@Override @Override
public void translate(GeyserSession session, ClientboundRecipeBookAddPacket packet) { public void translate(GeyserSession session, ClientboundRecipeBookAddPacket packet) {
//System.out.println(packet); System.out.println(packet);
int netId = session.getLastRecipeNetId().get(); int netId = session.getLastRecipeNetId().get();
Int2ObjectMap<List<String>> javaToBedrockRecipeIds = session.getJavaToBedrockRecipeIds(); Int2ObjectMap<List<String>> javaToBedrockRecipeIds = session.getJavaToBedrockRecipeIds();
Int2ObjectMap<GeyserRecipe> geyserRecipes = session.getCraftingRecipes();
CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket(); UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket();
@ -101,14 +105,17 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
List<String> bedrockRecipeIds = new ArrayList<>(); List<String> bedrockRecipeIds = new ArrayList<>();
ItemData output = bedrockRecipes.right(); ItemData output = bedrockRecipes.right();
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left(); List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
GeyserRecipe geyserRecipe = new GeyserShapedRecipe(shapedRecipe);
for (int i = 0; i < left.size(); i++) { for (int i = 0; i < left.size(); i++) {
List<ItemDescriptorWithCount> inputs = left.get(i); List<ItemDescriptorWithCount> inputs = left.get(i);
String recipeId = contents.id() + "_" + i; String recipeId = contents.id() + "_" + i;
int recipeNetworkId = netId++;
craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId, craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId,
shapedRecipe.width(), shapedRecipe.height(), inputs, shapedRecipe.width(), shapedRecipe.height(), inputs,
Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, false, RecipeUnlockingRequirement.INVALID)); Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, recipeNetworkId, false, RecipeUnlockingRequirement.INVALID));
recipesPacket.getUnlockedRecipes().add(recipeId); recipesPacket.getUnlockedRecipes().add(recipeId);
bedrockRecipeIds.add(recipeId); bedrockRecipeIds.add(recipeId);
geyserRecipes.put(recipeNetworkId, geyserRecipe);
} }
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
} }
@ -121,13 +128,16 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
List<String> bedrockRecipeIds = new ArrayList<>(); List<String> bedrockRecipeIds = new ArrayList<>();
ItemData output = bedrockRecipes.right(); ItemData output = bedrockRecipes.right();
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left(); List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
GeyserRecipe geyserRecipe = new GeyserShapelessRecipe(shapelessRecipe);
for (int i = 0; i < left.size(); i++) { for (int i = 0; i < left.size(); i++) {
List<ItemDescriptorWithCount> inputs = left.get(i); List<ItemDescriptorWithCount> inputs = left.get(i);
String recipeId = contents.id() + "_" + i; String recipeId = contents.id() + "_" + i;
int recipeNetworkId = netId++;
craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless(recipeId, craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless(recipeId,
inputs, Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, netId++, RecipeUnlockingRequirement.INVALID)); inputs, Collections.singletonList(output), UUID.randomUUID(), "crafting_table", 0, recipeNetworkId, RecipeUnlockingRequirement.INVALID));
recipesPacket.getUnlockedRecipes().add(recipeId); recipesPacket.getUnlockedRecipes().add(recipeId);
bedrockRecipeIds.add(recipeId); bedrockRecipeIds.add(recipeId);
geyserRecipes.put(recipeNetworkId, geyserRecipe);
} }
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds)); javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
} }

Datei anzeigen

@ -44,12 +44,15 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.InventoryUtils; import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerSetSlotPacket;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -104,9 +107,6 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
* Checks for a changed output slot in the crafting grid, and ensures Bedrock sees the recipe. * Checks for a changed output slot in the crafting grid, and ensures Bedrock sees the recipe.
*/ */
private static void updateCraftingGrid(GeyserSession session, int slot, ItemStack item, Inventory inventory, InventoryTranslator translator) { private static void updateCraftingGrid(GeyserSession session, int slot, ItemStack item, Inventory inventory, InventoryTranslator translator) {
if (true) {
return;
}
// Check if it's the crafting grid result slot. // Check if it's the crafting grid result slot.
if (slot != 0) { if (slot != 0) {
return; return;
@ -166,14 +166,13 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
ItemData[] ingredients = new ItemData[height * width]; ItemData[] ingredients = new ItemData[height * width];
//construct ingredient list and clear slots on client //construct ingredient list and clear slots on client
Ingredient[] javaIngredients = new Ingredient[height * width]; List<SlotDisplay> javaIngredients = new ArrayList<>(height * width);
int index = 0; int index = 0;
for (int row = firstRow; row < height + firstRow; row++) { for (int row = firstRow; row < height + firstRow; row++) {
for (int col = firstCol; col < width + firstCol; col++) { for (int col = firstCol; col < width + firstCol; col++) {
GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1); GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1);
ingredients[index] = geyserItemStack.getItemData(session); ingredients[index] = geyserItemStack.getItemData(session);
int[] items = new int[] {geyserItemStack.isEmpty() ? 0 : geyserItemStack.getJavaId()}; javaIngredients.add(geyserItemStack.isEmpty() ? new EmptySlotDisplay() : new ItemStackSlotDisplay(geyserItemStack.getItemStack()));
javaIngredients[index] = new Ingredient(new HolderSet(items));
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.UI); slotPacket.setContainerId(ContainerId.UI);
@ -185,7 +184,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
} }
// Cache this recipe so we know the client has received it // Cache this recipe so we know the client has received it
session.getCraftingRecipes().put(newRecipeId, new GeyserShapedRecipe(width, height, javaIngredients, item)); session.getCraftingRecipes().put(newRecipeId, new GeyserShapedRecipe(width, height, javaIngredients, new ItemStackSlotDisplay(item)));
CraftingDataPacket craftPacket = new CraftingDataPacket(); CraftingDataPacket craftPacket = new CraftingDataPacket();
craftPacket.getCraftingData().add(ShapedRecipeData.shaped( craftPacket.getCraftingData().add(ShapedRecipeData.shaped(

Datei anzeigen

@ -51,6 +51,8 @@ import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.inventory.InventoryTranslator;
@ -59,15 +61,21 @@ import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTransl
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient; import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.TagSlotDisplay;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundPickItemPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction; import java.util.function.IntFunction;
@ -441,6 +449,35 @@ public class InventoryUtils {
}; };
} }
/**
* Returns if the provided item stack would be accepted by the slot display.
*/
public static boolean acceptsAsInput(GeyserSession session, SlotDisplay slotDisplay, GeyserItemStack itemStack) {
if (slotDisplay instanceof EmptySlotDisplay) {
return itemStack.isEmpty();
}
if (slotDisplay instanceof CompositeSlotDisplay compositeSlotDisplay) {
if (compositeSlotDisplay.contents().size() == 1) {
return acceptsAsInput(session, compositeSlotDisplay.contents().get(0), itemStack);
}
return compositeSlotDisplay.contents().stream().anyMatch(aSlotDisplay -> acceptsAsInput(session, aSlotDisplay, itemStack));
}
if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) {
return itemStack.getJavaId() == itemSlotDisplay.item();
}
if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) {
ItemStack other = itemStackSlotDisplay.itemStack();
// Amount check might be flimsy?
return itemStack.getJavaId() == other.getId() && itemStack.getAmount() >= other.getAmount()
&& Objects.equals(itemStack.getComponents(), other.getDataComponents());
}
if (slotDisplay instanceof TagSlotDisplay tagSlotDisplay) {
return session.getTagCache().is(new Tag<>(JavaRegistries.ITEM, tagSlotDisplay.tag()), itemStack.asItem());
}
session.getGeyser().getLogger().warning("Unknown slot display type: " + slotDisplay);
return false;
}
/** /**
* Test all known recipes to find a valid match * Test all known recipes to find a valid match
* *
@ -462,91 +499,70 @@ public class InventoryUtils {
for (GeyserRecipe recipe : session.getCraftingRecipes().values()) { for (GeyserRecipe recipe : session.getCraftingRecipes().values()) {
if (recipe.isShaped()) { if (recipe.isShaped()) {
GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe; GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe;
if (output != null && !shapedRecipe.result().equals(output)) { if (output != null && !acceptsAsInput(session, shapedRecipe.result(), GeyserItemStack.from(output))) {
continue; continue;
} }
Ingredient[] ingredients = shapedRecipe.ingredients(); List<SlotDisplay> ingredients = shapedRecipe.ingredients();
if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.length) { if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.size()) {
continue; continue;
} }
if (!testShapedRecipe(ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { if (!testShapedRecipe(session, ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
Ingredient[] mirroredIngredients = new Ingredient[ingredients.length]; List<SlotDisplay> mirroredIngredients = new ArrayList<>(ingredients.size());
for (int row = 0; row < height; row++) { for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) { for (int col = 0; col < width; col++) {
mirroredIngredients[col + (row * width)] = ingredients[(width - 1 - col) + (row * width)]; mirroredIngredients.add(ingredients.get((width - 1 - col) + (row * width)));
} }
} }
if (Arrays.equals(ingredients, mirroredIngredients) || if (!ingredients.equals(mirroredIngredients) ||
!testShapedRecipe(mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) { !testShapedRecipe(session, mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
continue; continue;
} }
} }
} else { } else {
GeyserShapelessRecipe data = (GeyserShapelessRecipe) recipe; GeyserShapelessRecipe data = (GeyserShapelessRecipe) recipe;
if (output != null && !data.result().equals(output)) { if (output != null && !acceptsAsInput(session, data.result(), GeyserItemStack.from(output))) {
continue; continue;
} }
if (nonAirCount != data.ingredients().length) { if (nonAirCount != data.ingredients().size()) {
// There is an amount of items on the crafting table that is not the same as the ingredient count so this is invalid // There is an amount of items on the crafting table that is not the same as the ingredient count so this is invalid
continue; continue;
} }
for (int i = 0; i < data.ingredients().length; i++) { for (int i = 0; i < data.ingredients().size(); i++) {
Ingredient ingredient = data.ingredients()[i]; SlotDisplay slotDisplay = data.ingredients().get(i);
for (int item : ingredient.getValues().getHolders()) { // FIXME boolean inventoryHasItem = false;
boolean inventoryHasItem = false; // Iterate only over the crafting table to find this item
// Iterate only over the crafting table to find this item for (int row = firstRow; row < height + firstRow; row++) {
crafting: for (int col = firstCol; col < width + firstCol; col++) {
for (int row = firstRow; row < height + firstRow; row++) { GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
for (int col = firstCol; col < width + firstCol; col++) { if (acceptsAsInput(session, slotDisplay, geyserItemStack)) {
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); inventoryHasItem = true;
if (geyserItemStack.isEmpty()) { break;
inventoryHasItem = item == 0;
if (inventoryHasItem) {
break crafting;
}
} else if (item == geyserItemStack.getJavaId()) {
inventoryHasItem = true;
break crafting;
}
} }
} }
if (!inventoryHasItem) { }
continue recipes; if (!inventoryHasItem) {
} continue recipes;
} }
} }
} }
System.out.println("Found existing match for item: " + recipe);
return recipe; return recipe;
} }
return null; return null;
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted") @SuppressWarnings("BooleanMethodIsAlwaysInverted")
private static boolean testShapedRecipe(final Ingredient[] ingredients, final IntFunction<GeyserItemStack> inventoryGetter, private static boolean testShapedRecipe(final GeyserSession session, final List<SlotDisplay> ingredients, final IntFunction<GeyserItemStack> inventoryGetter,
final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) { final int gridDimensions, final int firstRow, final int height, final int firstCol, final int width) {
int ingredientIndex = 0; int ingredientIndex = 0;
for (int row = firstRow; row < height + firstRow; row++) { for (int row = firstRow; row < height + firstRow; row++) {
for (int col = firstCol; col < width + firstCol; col++) { for (int col = firstCol; col < width + firstCol; col++) {
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1); GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
Ingredient ingredient = ingredients[ingredientIndex++]; SlotDisplay slotDisplay = ingredients.get(ingredientIndex++);
int[] items = ingredient.getValues().getHolders(); // FIXME if (!acceptsAsInput(session, slotDisplay, geyserItemStack)) {
if (items.length == 0) { return false;
if (!geyserItemStack.isEmpty()) {
return false;
}
} else {
boolean inventoryHasItem = false;
for (int item : items) {
if (geyserItemStack.getJavaId() == item) {
inventoryHasItem = true;
break;
}
}
if (!inventoryHasItem) {
return false;
}
} }
} }
} }