Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-25 15:50:14 +01:00
On-the-fly smithing transform recipes
Dieser Commit ist enthalten in:
Ursprung
05f153c941
Commit
52679f9f81
@ -182,6 +182,13 @@ public class GeyserItemStack {
|
||||
return session.getItemMappings().getMapping(this.javaId);
|
||||
}
|
||||
|
||||
public SlotDisplay asSlotDisplay() {
|
||||
if (isEmpty()) {
|
||||
return new EmptySlotDisplay();
|
||||
}
|
||||
return new ItemStackSlotDisplay(this.getItemStack());
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (isEmpty()) {
|
||||
return Items.AIR;
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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 org.geysermc.mcprotocollib.protocol.data.game.recipe.display.SmithingRecipeDisplay;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
|
||||
|
||||
public record GeyserSmithingRecipe(SlotDisplay template,
|
||||
SlotDisplay base,
|
||||
SlotDisplay addition,
|
||||
SlotDisplay result) implements GeyserRecipe {
|
||||
public GeyserSmithingRecipe(SmithingRecipeDisplay display) {
|
||||
this(display.template(), display.base(), display.addition(), display.result());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShaped() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
@ -77,6 +78,7 @@ import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.CraftingRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
|
||||
@ -141,6 +143,7 @@ import org.geysermc.geyser.impl.camera.GeyserCameraData;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.BlockItem;
|
||||
@ -311,7 +314,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
||||
|
||||
@Setter
|
||||
private ScheduledFuture<?> craftingGridFuture;
|
||||
private ScheduledFuture<?> containerOutputFuture;
|
||||
|
||||
/**
|
||||
* Stores session collision
|
||||
@ -447,6 +450,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds;
|
||||
|
||||
private final Int2ObjectMap<GeyserRecipe> craftingRecipes;
|
||||
@Setter
|
||||
private Pair<CraftingRecipeData, GeyserRecipe> lastCreatedRecipe = null; // TODO try to prevent sending duplicate recipes
|
||||
private final AtomicInteger lastRecipeNetId;
|
||||
|
||||
/**
|
||||
@ -455,6 +460,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||
*/
|
||||
@Setter
|
||||
private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
|
||||
private final List<GeyserSmithingRecipe> smithingRecipes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Whether to work around 1.13's different behavior in villager trading menus.
|
||||
|
@ -33,6 +33,11 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
|
||||
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
public static final int TEMPLATE = 0;
|
||||
public static final int INPUT = 1;
|
||||
public static final int MATERIAL = 2;
|
||||
public static final int OUTPUT = 3;
|
||||
|
||||
public SmithingInventoryTranslator() {
|
||||
super(4, Blocks.SMITHING_TABLE, ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
|
||||
}
|
||||
@ -40,10 +45,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
|
||||
@Override
|
||||
public int bedrockSlotToJava(ItemStackRequestSlotData slotInfoData) {
|
||||
return switch (slotInfoData.getContainer()) {
|
||||
case SMITHING_TABLE_TEMPLATE -> 0;
|
||||
case SMITHING_TABLE_INPUT -> 1;
|
||||
case SMITHING_TABLE_MATERIAL -> 2;
|
||||
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> 3;
|
||||
case SMITHING_TABLE_TEMPLATE -> TEMPLATE;
|
||||
case SMITHING_TABLE_INPUT -> INPUT;
|
||||
case SMITHING_TABLE_MATERIAL -> MATERIAL;
|
||||
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> OUTPUT;
|
||||
default -> super.bedrockSlotToJava(slotInfoData);
|
||||
};
|
||||
}
|
||||
@ -51,10 +56,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
|
||||
@Override
|
||||
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
|
||||
return switch (slot) {
|
||||
case 0 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53);
|
||||
case 1 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
|
||||
case 2 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
|
||||
case 3 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
|
||||
case TEMPLATE -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53);
|
||||
case INPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
|
||||
case MATERIAL -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
|
||||
case OUTPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
|
||||
default -> super.javaSlotToBedrockContainer(slot);
|
||||
};
|
||||
}
|
||||
@ -62,10 +67,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
|
||||
@Override
|
||||
public int javaSlotToBedrock(int slot) {
|
||||
return switch (slot) {
|
||||
case 0 -> 53;
|
||||
case 1 -> 51;
|
||||
case 2 -> 52;
|
||||
case 3 -> 50;
|
||||
case TEMPLATE -> 53;
|
||||
case INPUT -> 51;
|
||||
case MATERIAL -> 52;
|
||||
case OUTPUT -> 50;
|
||||
default -> super.javaSlotToBedrock(slot);
|
||||
};
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ 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.inventory.recipe.GeyserSmithingRecipe;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.BedrockRequiresTagItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
@ -176,6 +177,8 @@ public class JavaRecipeBookAddTranslator extends PacketTranslator<ClientboundRec
|
||||
}
|
||||
}
|
||||
javaToBedrockRecipeIds.put(contents.id(), bedrockRecipeIds);
|
||||
session.getSmithingRecipes().add(new GeyserSmithingRecipe(smithingRecipe));
|
||||
System.out.println(new GeyserSmithingRecipe(smithingRecipe));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerId;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.RecipeUnlockingRequirement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
|
||||
@ -36,15 +37,17 @@ import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.PlayerInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.SmithingInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
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.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;
|
||||
@ -83,7 +86,11 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||
return;
|
||||
}
|
||||
|
||||
updateCraftingGrid(session, slot, packet.getItem(), inventory, translator);
|
||||
if (translator instanceof SmithingInventoryTranslator) {
|
||||
updateSmithingTableOutput(session, slot, packet.getItem(), inventory);
|
||||
} else {
|
||||
updateCraftingGrid(session, slot, packet.getItem(), inventory, translator);
|
||||
}
|
||||
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||
if (packet.getContainerId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||
@ -119,15 +126,15 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||
}
|
||||
|
||||
// Only process the most recent crafting grid result, and cancel the previous one.
|
||||
if (session.getCraftingGridFuture() != null) {
|
||||
session.getCraftingGridFuture().cancel(false);
|
||||
if (session.getContainerOutputFuture() != null) {
|
||||
session.getContainerOutputFuture().cancel(false);
|
||||
}
|
||||
|
||||
if (InventoryUtils.isEmpty(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.setCraftingGridFuture(session.scheduleInEventLoop(() -> {
|
||||
session.setContainerOutputFuture(session.scheduleInEventLoop(() -> {
|
||||
int offset = gridSize == 4 ? 28 : 32;
|
||||
int gridDimensions = gridSize == 4 ? 2 : 3;
|
||||
int firstRow = -1, height = -1;
|
||||
@ -172,7 +179,7 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||
for (int col = firstCol; col < width + firstCol; col++) {
|
||||
GeyserItemStack geyserItemStack = inventory.getItem(col + (row * gridDimensions) + 1);
|
||||
ingredients[index] = geyserItemStack.getItemData(session);
|
||||
javaIngredients.add(geyserItemStack.isEmpty() ? new EmptySlotDisplay() : new ItemStackSlotDisplay(geyserItemStack.getItemStack()));
|
||||
javaIngredients.add(geyserItemStack.asSlotDisplay());
|
||||
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
slotPacket.setContainerId(ContainerId.UI);
|
||||
@ -216,4 +223,74 @@ public class JavaContainerSetSlotTranslator extends PacketTranslator<Clientbound
|
||||
}
|
||||
}, 150, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
private static void updateSmithingTableOutput(GeyserSession session, int slot, ItemStack output, Inventory inventory) {
|
||||
if (slot != SmithingInventoryTranslator.OUTPUT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only process the most recent output result, and cancel the previous one.
|
||||
if (session.getContainerOutputFuture() != null) {
|
||||
session.getContainerOutputFuture().cancel(false);
|
||||
}
|
||||
|
||||
if (InventoryUtils.isEmpty(output)) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.setContainerOutputFuture(session.scheduleInEventLoop(() -> {
|
||||
GeyserItemStack template = inventory.getItem(SmithingInventoryTranslator.TEMPLATE);
|
||||
if (template.asItem() != Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) {
|
||||
// Technically we should probably also do this for custom items, but last I checked Bedrock doesn't even support that.
|
||||
return;
|
||||
}
|
||||
|
||||
GeyserItemStack input = inventory.getItem(SmithingInventoryTranslator.INPUT);
|
||||
GeyserItemStack material = inventory.getItem(SmithingInventoryTranslator.MATERIAL);
|
||||
GeyserItemStack geyserOutput = GeyserItemStack.from(output);
|
||||
|
||||
for (GeyserSmithingRecipe recipe : session.getSmithingRecipes()) {
|
||||
if (InventoryUtils.acceptsAsInput(session, recipe.result(), geyserOutput)
|
||||
&& InventoryUtils.acceptsAsInput(session, recipe.base(), input)
|
||||
&& InventoryUtils.acceptsAsInput(session, recipe.addition(), material)
|
||||
&& InventoryUtils.acceptsAsInput(session, recipe.template(), template)) {
|
||||
// The client already recognizes this item.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
session.getSmithingRecipes().add(new GeyserSmithingRecipe(
|
||||
template.asSlotDisplay(),
|
||||
input.asSlotDisplay(),
|
||||
material.asSlotDisplay(),
|
||||
new ItemStackSlotDisplay(output)
|
||||
));
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
ItemData bedrockAddition = ItemTranslator.translateToBedrock(session, material.getItemStack());
|
||||
|
||||
CraftingDataPacket craftPacket = new CraftingDataPacket();
|
||||
craftPacket.getCraftingData().add(SmithingTransformRecipeData.of(
|
||||
uuid.toString(),
|
||||
ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, template.getItemStack())),
|
||||
ItemDescriptorWithCount.fromItem(ItemTranslator.translateToBedrock(session, input.getItemStack())),
|
||||
ItemDescriptorWithCount.fromItem(bedrockAddition),
|
||||
ItemTranslator.translateToBedrock(session, output),
|
||||
"smithing_table",
|
||||
session.getLastRecipeNetId().incrementAndGet()
|
||||
));
|
||||
craftPacket.setCleanRecipes(false);
|
||||
session.sendUpstreamPacket(craftPacket);
|
||||
|
||||
// Just set one of the slots to air, then right back to its proper item.
|
||||
InventorySlotPacket slotPacket = new InventorySlotPacket();
|
||||
slotPacket.setContainerId(ContainerId.UI);
|
||||
slotPacket.setSlot(session.getInventoryTranslator().javaSlotToBedrock(SmithingInventoryTranslator.MATERIAL));
|
||||
slotPacket.setItem(ItemData.AIR);
|
||||
session.sendUpstreamPacket(slotPacket);
|
||||
|
||||
session.getInventoryTranslator().updateSlot(session, inventory, SmithingInventoryTranslator.MATERIAL);
|
||||
}, 150, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren