mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-28 07:20:28 +01:00
Re-implement on-the-fly recipe sending
This commit is contained in:
parent
344d40dd1f
commit
05f153c941
11 changed files with 170 additions and 98 deletions
|
@ -29,6 +29,7 @@ import lombok.*;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
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.component.DataComponentType;
|
||||
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;
|
||||
|
||||
|
@ -77,6 +82,20 @@ public class GeyserItemStack {
|
|||
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() {
|
||||
return isEmpty() ? 0 : javaId;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
package org.geysermc.geyser.inventory.recipe;
|
||||
|
||||
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}.
|
||||
|
@ -38,5 +38,5 @@ public interface GeyserRecipe {
|
|||
boolean isShaped();
|
||||
|
||||
@Nullable
|
||||
ItemStack result();
|
||||
SlotDisplay result();
|
||||
}
|
||||
|
|
|
@ -26,14 +26,16 @@
|
|||
package org.geysermc.geyser.inventory.recipe;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapedCraftingRecipeDisplay;
|
||||
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) {
|
||||
// this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult());
|
||||
// }
|
||||
public record GeyserShapedRecipe(int width, int height, List<SlotDisplay> ingredients, @Nullable SlotDisplay result) implements GeyserRecipe {
|
||||
|
||||
public GeyserShapedRecipe(ShapedCraftingRecipeDisplay data) {
|
||||
this(data.width(), data.height(), data.ingredients(), data.result());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShaped() {
|
||||
|
|
|
@ -26,14 +26,16 @@
|
|||
package org.geysermc.geyser.inventory.recipe;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapelessCraftingRecipeDisplay;
|
||||
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) {
|
||||
// this(data.getIngredients(), data.getResult());
|
||||
// }
|
||||
public record GeyserShapelessRecipe(List<SlotDisplay> ingredients, @Nullable SlotDisplay result) implements GeyserRecipe {
|
||||
|
||||
public GeyserShapelessRecipe(ShapelessCraftingRecipeDisplay data) {
|
||||
this(data.ingredients(), data.result());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShaped() {
|
||||
|
|
|
@ -31,8 +31,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
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.data.game.item.ItemStack;
|
||||
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++) {
|
||||
//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;
|
||||
}
|
||||
|
@ -118,7 +116,7 @@ public abstract class RecipeRegistryLoader implements RegistryLoader<String, Map
|
|||
//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;
|
||||
}
|
||||
|
|
|
@ -446,8 +446,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds;
|
||||
|
||||
@Setter
|
||||
private Int2ObjectMap<GeyserRecipe> craftingRecipes;
|
||||
private final Int2ObjectMap<GeyserRecipe> craftingRecipes;
|
||||
private final AtomicInteger lastRecipeNetId;
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,21 +25,40 @@
|
|||
|
||||
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 org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
|
||||
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.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.ItemStackResponseContainer;
|
||||
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.packet.ItemStackResponsePacket;
|
||||
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.ClickPlan;
|
||||
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.ItemUtils;
|
||||
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.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
|
||||
public abstract class InventoryTranslator {
|
||||
|
@ -642,8 +666,8 @@ public abstract class InventoryTranslator {
|
|||
}
|
||||
int gridDimensions = gridSize == 4 ? 2 : 3;
|
||||
|
||||
Ingredient[] ingredients = new Ingredient[0];
|
||||
ItemStack output = null;
|
||||
List<SlotDisplay> ingredients = Collections.emptyList();
|
||||
SlotDisplay output = null;
|
||||
int recipeWidth = 0;
|
||||
int ingRemaining = 0;
|
||||
int ingredientIndex = -1;
|
||||
|
@ -697,7 +721,7 @@ public abstract class InventoryTranslator {
|
|||
ingredients = shapelessRecipe.ingredients();
|
||||
recipeWidth = gridDimensions;
|
||||
output = shapelessRecipe.result();
|
||||
if (ingredients.length > gridSize) {
|
||||
if (ingredients.size() > gridSize) {
|
||||
return rejectRequest(request);
|
||||
}
|
||||
}
|
||||
|
@ -728,11 +752,11 @@ public abstract class InventoryTranslator {
|
|||
craftState = CraftState.INGREDIENTS;
|
||||
|
||||
if (ingRemaining == 0) {
|
||||
while (++ingredientIndex < ingredients.length) {
|
||||
// if (ingredients[ingredientIndex].getOptions().length != 0) {
|
||||
// ingRemaining = timesCrafted;
|
||||
// break;
|
||||
// }
|
||||
while (++ingredientIndex < ingredients.size()) {
|
||||
if (!(ingredients.get(ingredientIndex) instanceof EmptySlotDisplay)) { // TODO I guess can technically other options be empty?
|
||||
ingRemaining = timesCrafted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ import java.util.UUID;
|
|||
import static org.geysermc.geyser.util.InventoryUtils.LAST_RECIPE_NET_ID;
|
||||
|
||||
@Translator(packet = ClientboundFinishConfigurationPacket.class)
|
||||
public class JavaFinishConfigurationPacketTranslator extends PacketTranslator<ClientboundFinishConfigurationPacket> {
|
||||
public class JavaFinishConfigurationTranslator extends PacketTranslator<ClientboundFinishConfigurationPacket> {
|
||||
/**
|
||||
* 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()));
|
||||
if (session.isSentSpawnPacket()) {
|
||||
session.getUpstream().sendPacket(craftingDataPacket);
|
||||
session.getCraftingRecipes().clear();
|
||||
session.getJavaToBedrockRecipeIds().clear();
|
||||
session.getStonecutterRecipes().clear();
|
||||
} else {
|
||||
session.getUpstream().queuePostStartGamePacket(craftingDataPacket);
|
||||
}
|
|
@ -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.packet.CraftingDataPacket;
|
||||
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.type.BedrockRequiresTagItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
|
@ -79,9 +82,10 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundRecipeBookAddPacket packet) {
|
||||
//System.out.println(packet);
|
||||
System.out.println(packet);
|
||||
int netId = session.getLastRecipeNetId().get();
|
||||
Int2ObjectMap<List<String>> javaToBedrockRecipeIds = session.getJavaToBedrockRecipeIds();
|
||||
Int2ObjectMap<GeyserRecipe> geyserRecipes = session.getCraftingRecipes();
|
||||
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
|
||||
|
||||
UnlockedRecipesPacket recipesPacket = new UnlockedRecipesPacket();
|
||||
|
@ -101,14 +105,17 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
|||
List<String> bedrockRecipeIds = new ArrayList<>();
|
||||
ItemData output = bedrockRecipes.right();
|
||||
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
|
||||
GeyserRecipe geyserRecipe = new GeyserShapedRecipe(shapedRecipe);
|
||||
for (int i = 0; i < left.size(); i++) {
|
||||
List<ItemDescriptorWithCount> inputs = left.get(i);
|
||||
String recipeId = contents.id() + "_" + i;
|
||||
int recipeNetworkId = netId++;
|
||||
craftingDataPacket.getCraftingData().add(ShapedRecipeData.shaped(recipeId,
|
||||
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);
|
||||
bedrockRecipeIds.add(recipeId);
|
||||
geyserRecipes.put(recipeNetworkId, geyserRecipe);
|
||||
}
|
||||
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
|
||||
}
|
||||
|
@ -121,13 +128,16 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
|||
List<String> bedrockRecipeIds = new ArrayList<>();
|
||||
ItemData output = bedrockRecipes.right();
|
||||
List<List<ItemDescriptorWithCount>> left = bedrockRecipes.left();
|
||||
GeyserRecipe geyserRecipe = new GeyserShapelessRecipe(shapelessRecipe);
|
||||
for (int i = 0; i < left.size(); i++) {
|
||||
List<ItemDescriptorWithCount> inputs = left.get(i);
|
||||
String recipeId = contents.id() + "_" + i;
|
||||
int recipeNetworkId = netId++;
|
||||
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);
|
||||
bedrockRecipeIds.add(recipeId);
|
||||
geyserRecipes.put(recipeNetworkId, geyserRecipe);
|
||||
}
|
||||
javaToBedrockRecipeIds.put(contents.id(), List.copyOf(bedrockRecipeIds));
|
||||
}
|
||||
|
|
|
@ -44,12 +44,15 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
|||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
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.Ingredient;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
if (slot != 0) {
|
||||
return;
|
||||
|
@ -166,14 +166,13 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
|||
|
||||
ItemData[] ingredients = new ItemData[height * width];
|
||||
//construct ingredient list and clear slots on client
|
||||
Ingredient[] javaIngredients = new Ingredient[height * width];
|
||||
List<SlotDisplay> javaIngredients = new ArrayList<>(height * width);
|
||||
int index = 0;
|
||||
for (int row = firstRow; row < height + firstRow; row++) {
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1);
|
||||
ingredients[index] = geyserItemStack.getItemData(session);
|
||||
int[] items = new int[] {geyserItemStack.isEmpty() ? 0 : geyserItemStack.getJavaId()};
|
||||
javaIngredients[index] = new Ingredient(new HolderSet(items));
|
||||
javaIngredients.add(geyserItemStack.isEmpty() ? new EmptySlotDisplay() : new ItemStackSlotDisplay(geyserItemStack.getItemStack()));
|
||||
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
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
|
||||
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();
|
||||
craftPacket.getCraftingData().add(ShapedRecipeData.shaped(
|
||||
|
|
|
@ -51,6 +51,8 @@ import org.geysermc.geyser.level.BedrockDimension;
|
|||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
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.GeyserLocale;
|
||||
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.item.ItemStack;
|
||||
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.ServerboundPickItemPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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
|
||||
*
|
||||
|
@ -462,91 +499,70 @@ public class InventoryUtils {
|
|||
for (GeyserRecipe recipe : session.getCraftingRecipes().values()) {
|
||||
if (recipe.isShaped()) {
|
||||
GeyserShapedRecipe shapedRecipe = (GeyserShapedRecipe) recipe;
|
||||
if (output != null && !shapedRecipe.result().equals(output)) {
|
||||
if (output != null && !acceptsAsInput(session, shapedRecipe.result(), GeyserItemStack.from(output))) {
|
||||
continue;
|
||||
}
|
||||
Ingredient[] ingredients = shapedRecipe.ingredients();
|
||||
if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.length) {
|
||||
List<SlotDisplay> ingredients = shapedRecipe.ingredients();
|
||||
if (shapedRecipe.width() != width || shapedRecipe.height() != height || width * height != ingredients.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!testShapedRecipe(ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
|
||||
Ingredient[] mirroredIngredients = new Ingredient[ingredients.length];
|
||||
if (!testShapedRecipe(session, ingredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
|
||||
List<SlotDisplay> mirroredIngredients = new ArrayList<>(ingredients.size());
|
||||
for (int row = 0; row < height; row++) {
|
||||
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) ||
|
||||
!testShapedRecipe(mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
|
||||
if (!ingredients.equals(mirroredIngredients) ||
|
||||
!testShapedRecipe(session, mirroredIngredients, inventoryGetter, gridDimensions, firstRow, height, firstCol, width)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GeyserShapelessRecipe data = (GeyserShapelessRecipe) recipe;
|
||||
if (output != null && !data.result().equals(output)) {
|
||||
if (output != null && !acceptsAsInput(session, data.result(), GeyserItemStack.from(output))) {
|
||||
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
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < data.ingredients().length; i++) {
|
||||
Ingredient ingredient = data.ingredients()[i];
|
||||
for (int item : ingredient.getValues().getHolders()) { // FIXME
|
||||
boolean inventoryHasItem = false;
|
||||
// Iterate only over the crafting table to find this item
|
||||
crafting:
|
||||
for (int row = firstRow; row < height + firstRow; row++) {
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
||||
if (geyserItemStack.isEmpty()) {
|
||||
inventoryHasItem = item == 0;
|
||||
if (inventoryHasItem) {
|
||||
break crafting;
|
||||
}
|
||||
} else if (item == geyserItemStack.getJavaId()) {
|
||||
inventoryHasItem = true;
|
||||
break crafting;
|
||||
}
|
||||
for (int i = 0; i < data.ingredients().size(); i++) {
|
||||
SlotDisplay slotDisplay = data.ingredients().get(i);
|
||||
boolean inventoryHasItem = false;
|
||||
// Iterate only over the crafting table to find this item
|
||||
for (int row = firstRow; row < height + firstRow; row++) {
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
||||
if (acceptsAsInput(session, slotDisplay, geyserItemStack)) {
|
||||
inventoryHasItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inventoryHasItem) {
|
||||
continue recipes;
|
||||
}
|
||||
}
|
||||
if (!inventoryHasItem) {
|
||||
continue recipes;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("Found existing match for item: " + recipe);
|
||||
return recipe;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@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) {
|
||||
int ingredientIndex = 0;
|
||||
for (int row = firstRow; row < height + firstRow; row++) {
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventoryGetter.apply(col + (row * gridDimensions) + 1);
|
||||
Ingredient ingredient = ingredients[ingredientIndex++];
|
||||
int[] items = ingredient.getValues().getHolders(); // FIXME
|
||||
if (items.length == 0) {
|
||||
if (!geyserItemStack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
boolean inventoryHasItem = false;
|
||||
for (int item : items) {
|
||||
if (geyserItemStack.getJavaId() == item) {
|
||||
inventoryHasItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inventoryHasItem) {
|
||||
return false;
|
||||
}
|
||||
SlotDisplay slotDisplay = ingredients.get(ingredientIndex++);
|
||||
if (!acceptsAsInput(session, slotDisplay, geyserItemStack)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue