|
28 | 28 | import com.google.gson.JsonObject; |
29 | 29 | import io.netty.channel.Channel; |
30 | 30 | import io.netty.channel.EventLoop; |
31 | | -import it.unimi.dsi.fastutil.Pair; |
32 | 31 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; |
| 32 | +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; |
33 | 33 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; |
34 | 34 | import it.unimi.dsi.fastutil.objects.Object2IntMap; |
35 | 35 | import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; |
36 | 36 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; |
| 37 | +import it.unimi.dsi.fastutil.objects.ObjectIntPair; |
37 | 38 | import lombok.AccessLevel; |
38 | 39 | import lombok.Getter; |
39 | 40 | import lombok.Setter; |
|
76 | 77 | import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; |
77 | 78 | import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; |
78 | 79 | import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition; |
| 80 | +import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; |
79 | 81 | import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; |
80 | 82 | import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; |
81 | | -import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.CraftingRecipeData; |
| 83 | +import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.RecipeUnlockingRequirement; |
| 84 | +import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapelessRecipeData; |
| 85 | +import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.SmithingTransformRecipeData; |
| 86 | +import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.DefaultDescriptor; |
| 87 | +import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; |
82 | 88 | import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; |
83 | 89 | import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; |
84 | 90 | import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket; |
85 | 91 | import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket; |
86 | 92 | import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; |
| 93 | +import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; |
87 | 94 | import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; |
88 | 95 | import org.cloudburstmc.protocol.bedrock.packet.DimensionDataPacket; |
89 | 96 | import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; |
|
150 | 157 | import org.geysermc.geyser.inventory.recipe.GeyserRecipe; |
151 | 158 | import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe; |
152 | 159 | import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData; |
| 160 | +import org.geysermc.geyser.inventory.recipe.RecipeUtil; |
| 161 | +import org.geysermc.geyser.inventory.recipe.TrimRecipe; |
153 | 162 | import org.geysermc.geyser.item.Items; |
154 | 163 | import org.geysermc.geyser.item.type.BlockItem; |
155 | 164 | import org.geysermc.geyser.level.BedrockDimension; |
|
158 | 167 | import org.geysermc.geyser.network.netty.LocalSession; |
159 | 168 | import org.geysermc.geyser.registry.Registries; |
160 | 169 | import org.geysermc.geyser.registry.type.BlockMappings; |
| 170 | +import org.geysermc.geyser.registry.type.ItemMapping; |
161 | 171 | import org.geysermc.geyser.registry.type.ItemMappings; |
162 | 172 | import org.geysermc.geyser.session.auth.AuthData; |
163 | 173 | import org.geysermc.geyser.session.auth.BedrockClientData; |
|
190 | 200 | import org.geysermc.geyser.skin.SkinManager; |
191 | 201 | import org.geysermc.geyser.text.GeyserLocale; |
192 | 202 | import org.geysermc.geyser.translator.inventory.InventoryTranslator; |
| 203 | +import org.geysermc.geyser.translator.item.ItemTranslator; |
193 | 204 | import org.geysermc.geyser.translator.text.MessageTranslator; |
194 | 205 | import org.geysermc.geyser.util.ChunkUtils; |
195 | 206 | import org.geysermc.geyser.util.CooldownUtils; |
|
214 | 225 | import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference; |
215 | 226 | import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; |
216 | 227 | import org.geysermc.mcprotocollib.protocol.data.game.entity.player.ResolvableProfile; |
| 228 | +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; |
217 | 229 | import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility; |
218 | 230 | import org.geysermc.mcprotocollib.protocol.data.game.setting.ParticleStatus; |
219 | 231 | import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart; |
@@ -521,16 +533,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { |
521 | 533 | private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds; |
522 | 534 |
|
523 | 535 | private final Int2ObjectMap<GeyserRecipe> craftingRecipes; |
524 | | - @Setter |
525 | | - private Pair<CraftingRecipeData, GeyserRecipe> lastCreatedRecipe = null; // TODO try to prevent sending duplicate recipes |
526 | 536 | private final AtomicInteger lastRecipeNetId; |
527 | 537 |
|
528 | 538 | /** |
529 | 539 | * Saves a list of all stonecutter recipes, for use in a stonecutter inventory. |
530 | 540 | * The key is the Bedrock recipe net ID; the values are their respective output and button ID. |
531 | 541 | */ |
532 | 542 | @Setter |
533 | | - private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes; |
| 543 | + private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes = Int2ObjectMaps.emptyMap(); |
534 | 544 | private final List<GeyserSmithingRecipe> smithingRecipes = new ArrayList<>(); |
535 | 545 |
|
536 | 546 | /** |
@@ -2525,4 +2535,57 @@ public void sendNetworkLatencyStackPacket(long timestamp, boolean ensureEventLoo |
2525 | 2535 | public String getDebugInfo() { |
2526 | 2536 | return "Username: %s, DeviceOs: %s, Version: %s".formatted(bedrockUsername(), platform(), version()); |
2527 | 2537 | } |
| 2538 | + |
| 2539 | + public CraftingDataPacket getCraftingDataPacket() { |
| 2540 | + CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); |
| 2541 | + craftingDataPacket.setCleanRecipes(true); |
| 2542 | + craftingDataPacket.getCraftingData().addAll(RecipeUtil.CARTOGRAPHY_RECIPES); |
| 2543 | + // Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand. |
| 2544 | + craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(getUpstream().getProtocolVersion())); |
| 2545 | + for (GeyserRecipe recipe : craftingRecipes.values()) { |
| 2546 | + craftingDataPacket.getCraftingData().addAll(recipe.asRecipeData(this)); |
| 2547 | + } |
| 2548 | + for (GeyserSmithingRecipe recipe : smithingRecipes) { |
| 2549 | + craftingDataPacket.getCraftingData().addAll(recipe.asRecipeData(this)); |
| 2550 | + } |
| 2551 | + if (oldSmithingTable) { |
| 2552 | + ItemMapping template = itemMappings.getStoredItems().upgradeTemplate(); |
| 2553 | + |
| 2554 | + for (ObjectIntPair<String> identifierAndNetId : TrimRecipe.NETHERITE_UPGRADES) { |
| 2555 | + craftingDataPacket.getCraftingData().add(SmithingTransformRecipeData.of(identifierAndNetId.left() + "_smithing", |
| 2556 | + getDescriptorFromId(this, template.getBedrockIdentifier()), |
| 2557 | + getDescriptorFromId(this, identifierAndNetId.left().replace("netherite", "diamond")), |
| 2558 | + getDescriptorFromId(this, "minecraft:netherite_ingot"), |
| 2559 | + ItemData.builder().definition(Objects.requireNonNull(itemMappings.getDefinition(identifierAndNetId.left()))).count(1).build(), |
| 2560 | + "smithing_table", |
| 2561 | + identifierAndNetId.rightInt())); |
| 2562 | + } |
| 2563 | + } else { |
| 2564 | + craftingDataPacket.getCraftingData().add(TrimRecipe.RECIPE); |
| 2565 | + } |
| 2566 | + for (Int2ObjectMap.Entry<GeyserStonecutterData> recipe : stonecutterRecipes.int2ObjectEntrySet()) { |
| 2567 | + int buttonId = recipe.getValue().buttonId(); |
| 2568 | + int javaInput = recipe.getValue().input(); |
| 2569 | + ItemMapping mapping = itemMappings.getMapping(javaInput); |
| 2570 | + ItemDescriptorWithCount descriptor = new ItemDescriptorWithCount(new DefaultDescriptor(mapping.getBedrockDefinition(), mapping.getBedrockData()), 1); |
| 2571 | + ItemStack javaOutput = recipe.getValue().output(); |
| 2572 | + ItemData output = ItemTranslator.translateToBedrock(this, javaOutput); |
| 2573 | + int recipeNetId = recipe.getIntKey(); |
| 2574 | + UUID uuid = UUID.randomUUID(); |
| 2575 | + // We need to register stonecutting recipes, so they show up on Bedrock |
| 2576 | + // (Implementation note: recipe ID creates the order which stonecutting recipes are shown in stonecutter) |
| 2577 | + craftingDataPacket.getCraftingData().add(ShapelessRecipeData.shapeless("stonecutter_" + javaInput + "_" + buttonId, |
| 2578 | + Collections.singletonList(descriptor), Collections.singletonList(output), uuid, "stonecutter", 0, recipeNetId, RecipeUnlockingRequirement.INVALID)); |
| 2579 | + } |
| 2580 | + return craftingDataPacket; |
| 2581 | + } |
| 2582 | + |
| 2583 | + private ItemDescriptorWithCount getDescriptorFromId(GeyserSession session, String bedrockId) { |
| 2584 | + ItemDefinition bedrockDefinition = session.getItemMappings().getDefinition(bedrockId); |
| 2585 | + if (bedrockDefinition != null) { |
| 2586 | + return ItemDescriptorWithCount.fromItem(ItemData.builder().definition(bedrockDefinition).count(1).build()); |
| 2587 | + } |
| 2588 | + GeyserImpl.getInstance().getLogger().debug("Unable to find item with identifier " + bedrockId); |
| 2589 | + return ItemDescriptorWithCount.EMPTY; |
| 2590 | + } |
2528 | 2591 | } |
0 commit comments