diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/RegistryEntry.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/RegistryEntry.java index 36ab302b0..c210fd82d 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/RegistryEntry.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/RegistryEntry.java @@ -36,4 +36,8 @@ public record RegistryEntry(String key, @Nullable Tag tag) { public RegistryEntry withKey(final String key) { return new RegistryEntry(key, tag != null ? tag.copy() : null); } + + public RegistryEntry copy() { + return new RegistryEntry(key, tag != null ? tag.copy() : null); + } } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_21_2.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_21_2.java index b9f3028a4..229bcc815 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_21_2.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/entities/EntityTypes1_21_2.java @@ -118,6 +118,8 @@ public enum EntityTypes1_21_2 implements EntityType { MANGROVE_CHEST_BOAT(ABSTRACT_CHEST_BOAT), OAK_BOAT(ABSTRACT_BOAT), OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT), + PALE_OAK_BOAT(ABSTRACT_BOAT), + PALE_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT), SPRUCE_BOAT(ABSTRACT_BOAT), SPRUCE_CHEST_BOAT(ABSTRACT_CHEST_BOAT), @@ -209,6 +211,8 @@ public enum EntityTypes1_21_2 implements EntityType { BREEZE(ABSTRACT_MONSTER), ZOGLIN(ABSTRACT_MONSTER), WARDEN(ABSTRACT_MONSTER), + CREAKING(ABSTRACT_MONSTER), + CREAKING_TRANSIENT(ABSTRACT_MONSTER), ABSTRACT_SKELETON(ABSTRACT_MONSTER, null), SKELETON(ABSTRACT_SKELETON), diff --git a/api/src/main/java/com/viaversion/viaversion/api/protocol/version/ProtocolVersion.java b/api/src/main/java/com/viaversion/viaversion/api/protocol/version/ProtocolVersion.java index 2aaf3fdc8..dec05c01e 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/protocol/version/ProtocolVersion.java +++ b/api/src/main/java/com/viaversion/viaversion/api/protocol/version/ProtocolVersion.java @@ -86,7 +86,7 @@ public class ProtocolVersion implements Comparable { public static final ProtocolVersion v1_20_3 = register(765, "1.20.3-1.20.4", new SubVersionRange("1.20", 3, 4)); public static final ProtocolVersion v1_20_5 = register(766, "1.20.5-1.20.6", new SubVersionRange("1.20", 5, 6)); public static final ProtocolVersion v1_21 = register(767, "1.21-1.21.1", new SubVersionRange("1.21", 0, 1)); - public static final ProtocolVersion v1_21_2 = register(768, 211, "1.21.2"); + public static final ProtocolVersion v1_21_2 = register(768, 212, "1.21.2"); public static final ProtocolVersion unknown = new ProtocolVersion(VersionType.SPECIAL, -1, -1, "UNKNOWN", null); public static ProtocolVersion register(int version, String name) { diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/misc/ParticleType.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/misc/ParticleType.java index f02573c07..aac0549a8 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/misc/ParticleType.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/misc/ParticleType.java @@ -137,6 +137,12 @@ public class ParticleType extends DynamicType { particle.add(Types.VAR_INT, Types.VAR_INT.readPrimitive(buf)); // Delay }; public static final DataReader COLOR = (buf, particle) -> particle.add(Types.INT, buf.readInt()); + public static final DataReader TRAIL = (buf, particle) -> { + particle.add(Types.VAR_INT, Types.VAR_INT.readPrimitive(buf)); // Target X + particle.add(Types.VAR_INT, Types.VAR_INT.readPrimitive(buf)); // Target Y + particle.add(Types.VAR_INT, Types.VAR_INT.readPrimitive(buf)); // Target Z + particle.add(Types.INT, buf.readInt()); // Color + }; public static DataReader item(Type item) { return (buf, particle) -> particle.add(item, item.read(buf)); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/template/Protocol1_99To_98.java b/common/src/main/java/com/viaversion/viaversion/protocols/template/Protocol1_99To_98.java index d79a08a0c..401597e0b 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/template/Protocol1_99To_98.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/template/Protocol1_99To_98.java @@ -105,16 +105,19 @@ final class Protocol1_99To_98 extends AbstractProtocol { @@ -88,7 +88,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< protocol.registerClientbound(ClientboundPackets1_21.CONTAINER_SET_CONTENT, wrapper -> { updateContainerId(wrapper); wrapper.passthrough(Types.VAR_INT); // State id - Item[] items = wrapper.read(itemArrayType()); + final Item[] items = wrapper.read(itemArrayType()); wrapper.write(mappedItemArrayType(), items); for (int i = 0; i < items.length; i++) { items[i] = handleItemToClient(wrapper.user(), items[i]); @@ -104,10 +104,8 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< protocol.registerClientbound(ClientboundPackets1_21.CONTAINER_CLOSE, this::updateContainerId); protocol.registerClientbound(ClientboundPackets1_21.CONTAINER_SET_DATA, this::updateContainerId); protocol.registerClientbound(ClientboundPackets1_21.HORSE_SCREEN_OPEN, this::updateContainerId); - protocol.registerClientbound(ClientboundPackets1_21.PLACE_GHOST_RECIPE, this::updateContainerId); protocol.registerClientbound(ClientboundPackets1_21.SET_CARRIED_ITEM, ClientboundPackets1_21_2.SET_HELD_SLOT); protocol.registerServerbound(ServerboundPackets1_21_2.CONTAINER_CLOSE, this::updateContainerIdServerbound); - protocol.registerServerbound(ServerboundPackets1_21_2.PLACE_RECIPE, this::updateContainerIdServerbound); protocol.registerServerbound(ServerboundPackets1_21_2.CONTAINER_CLICK, wrapper -> { updateContainerIdServerbound(wrapper); wrapper.passthrough(Types.VAR_INT); // State id @@ -121,6 +119,26 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< } passthroughServerboundItem(wrapper); }); + protocol.registerClientbound(ClientboundPackets1_21.PLACE_GHOST_RECIPE, wrapper -> { + this.updateContainerId(wrapper); + + final String recipe = wrapper.read(Types.STRING); + wrapper.write(Types.VAR_INT, recipeDisplay(recipe)); // TODO + }); + protocol.registerServerbound(ServerboundPackets1_21_2.PLACE_RECIPE, wrapper -> { + this.updateContainerIdServerbound(wrapper); + + final String recipe = wrapper.read(Types.STRING); + wrapper.write(Types.VAR_INT, 0); // TODO Display id, from recipe packet + wrapper.cancel(); + }); + protocol.registerServerbound(ServerboundPackets1_21_2.RECIPE_BOOK_SEEN_RECIPE, wrapper -> { + this.updateContainerIdServerbound(wrapper); + + final String recipe = wrapper.read(Types.STRING); + wrapper.write(Types.VAR_INT, 0); // TODO Display id, from recipe packet + wrapper.cancel(); + }); protocol.registerServerbound(ServerboundPackets1_21_2.USE_ITEM_ON, wrapper -> { wrapper.passthrough(Types.VAR_INT); // Hand @@ -174,68 +192,64 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< new SoundRewriter<>(protocol).soundHolderHandler().handle(wrapper); }); - new RecipeRewriter1_20_3<>(protocol) { - @Override - protected void handleIngredient(final PacketWrapper wrapper) { - wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); - } + protocol.registerClientbound(ClientboundPackets1_21.UPDATE_RECIPES, wrapper -> { + final RecipeRewriter rewriter = new RecipeRewriter(protocol); + wrapper.cancel(); // TODO - @Override - public void handleCraftingShaped(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - final int width = wrapper.passthrough(Types.VAR_INT); - final int height = wrapper.passthrough(Types.VAR_INT); - final int ingredients = width * height; + final int size = wrapper.passthrough(Types.VAR_INT); + int newSize = size; + for (int i = 0; i < size; i++) { + final String recipeIdentifier = wrapper.read(Types.STRING); - wrapper.write(Types.VAR_INT, ingredients); - for (int i = 0; i < ingredients; i++) { - wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); - } - - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); // Result - wrapper.passthrough(Types.BOOLEAN); // Show notification - } - - @Override - public void handleCraftingShapeless(final PacketWrapper wrapper) { - wrapper.passthrough(Types.STRING); // Group - wrapper.passthrough(Types.VAR_INT); // Crafting book category - - final int ingredients = wrapper.read(Types.VAR_INT); - final HolderSet[] ingredient = new HolderSet[ingredients]; - for (int i = 0; i < ingredients; i++) { - ingredient[i] = ingredient(wrapper); - } - - wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); - - // Also moved below here - wrapper.write(Types.VAR_INT, ingredients); - for (final HolderSet item : ingredient) { - wrapper.write(Types.HOLDER_SET, item); - } - } - - private HolderSet ingredient(final PacketWrapper wrapper) { - final Item[] items = wrapper.read(itemArrayType()); - final int[] ids = new int[items.length]; - for (int i = 0; i < items.length; i++) { - final Item item = rewrite(wrapper.user(), items[i]); - ids[i] = item.identifier(); - } - return HolderSet.of(ids); - } - - @Override - public void handleRecipeType(final PacketWrapper wrapper, final String type) { - if (type.equals("crafting_special_suspiciousstew") || type.equals("crafting_special_shulkerboxcoloring")) { - wrapper.read(Types.VAR_INT); // Crafting book category + final FullMappings recipeSerializerMappings = protocol.getMappingData().getRecipeSerializerMappings(); + final int typeId = wrapper.read(Types.VAR_INT); + final int mappedId = recipeSerializerMappings.getNewId(typeId); + if (mappedId != -1) { + wrapper.write(Types.STRING, recipeIdentifier); + wrapper.write(Types.VAR_INT, mappedId); } else { - super.handleRecipeType(wrapper, type); + wrapper.set(Types.VAR_INT, 0, --newSize); } + + rewriter.handleRecipeType(wrapper, Key.stripMinecraftNamespace(recipeSerializerMappings.identifier(typeId))); // Use the original } - }.register1_20_5(ClientboundPackets1_21.UPDATE_RECIPES); + }); + + protocol.registerClientbound(ClientboundPackets1_21.RECIPE, ClientboundPackets1_21_2.RECIPE_BOOK_ADD, wrapper -> { + final int state = wrapper.passthrough(Types.VAR_INT); + + // Pairs of open + filtering for: Crafting, furnace, blast furnace, smoker + final PacketWrapper settingsPacket = wrapper.create(ClientboundPackets1_21_2.RECIPE_BOOK_SETTINGS); + for (int i = 0; i < 4 * 2; i++) { + settingsPacket.write(Types.BOOLEAN, wrapper.read(Types.BOOLEAN)); + } + settingsPacket.send(Protocol1_21To1_21_2.class); + + // TODO + wrapper.cancel(); + + final String[] recipes = wrapper.passthrough(Types.STRING_ARRAY); + Set toHighlight = Set.of(); + if (state == 0) { // Init (1=Add, 2=Remove) + final String[] highlightRecipes = wrapper.passthrough(Types.STRING_ARRAY); + toHighlight = Set.of(highlightRecipes); + } + + wrapper.write(Types.VAR_INT, recipes.length); + for (final String recipe : recipes) { + // Display id + // Display type + // Optional group + // Category type + // Item contents + + byte flags = 0; + if (toHighlight.contains(recipe)) { + flags |= (1 << 1); // Highlight + } + wrapper.write(Types.BYTE, flags); + } + }); } @Override @@ -252,6 +266,18 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< return item; } + private int recipeDisplay(String type) { + type = Key.stripMinecraftNamespace(type); + return switch (type) { + case "crafting_shapeless" -> 0; + case "crafting_shaped" -> 1; + case "furnace" -> 2; + case "stonecutter" -> 3; + case "smithing" -> 4; + default -> 0; + }; + } + private void updateContainerId(final PacketWrapper wrapper) { // Container id handling was always a bit whack with most reading them as unsigned bytes, some as bytes, some already as var ints. // In VV they're generally read as unsigned bytesto not have to look the type up every time, but we need to make sure they're diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java index d4086183e..d6f518766 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java @@ -18,9 +18,6 @@ package com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter; import com.viaversion.nbt.tag.CompoundTag; -import com.viaversion.nbt.tag.ListTag; -import com.viaversion.nbt.tag.StringTag; -import com.viaversion.viaversion.api.data.FullMappings; import com.viaversion.viaversion.api.minecraft.RegistryEntry; import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_2; @@ -33,12 +30,11 @@ import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfi import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21; import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21; import com.viaversion.viaversion.protocols.v1_21to1_21_2.Protocol1_21To1_21_2; +import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ClientVehicleStorage; import com.viaversion.viaversion.rewriter.EntityRewriter; -import com.viaversion.viaversion.util.Key; -import com.viaversion.viaversion.util.TagUtil; -import java.util.Arrays; +import com.viaversion.viaversion.rewriter.RegistryDataRewriter; public final class EntityPacketRewriter1_21_2 extends EntityRewriter { @@ -80,32 +76,8 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter { - final String registryKey = Key.stripMinecraftNamespace(wrapper.passthrough(Types.STRING)); - RegistryEntry[] entries = wrapper.read(Types.REGISTRY_ENTRY_ARRAY); - if (registryKey.equals("enchantment")) { - updateEnchantmentAttributes(entries, protocol.getMappingData().getAttributeMappings()); - } else if (registryKey.equals("damage_type")) { - // Add new damage types - final int length = entries.length; - entries = Arrays.copyOf(entries, length + 2); - - final CompoundTag enderpearlData = new CompoundTag(); - enderpearlData.putString("scaling", "when_caused_by_living_non_player"); - enderpearlData.putString("message_id", "fall"); - enderpearlData.putFloat("exhaustion", 0.0F); - entries[length] = new RegistryEntry("minecraft:ender_pearl", enderpearlData); - - final CompoundTag maceSmashData = new CompoundTag(); - maceSmashData.putString("scaling", "when_caused_by_living_non_player"); - maceSmashData.putString("message_id", "mace_smash"); - maceSmashData.putFloat("exhaustion", 0.1F); - entries[length + 1] = new RegistryEntry("minecraft:mace_smash", maceSmashData); - } - - wrapper.write(Types.REGISTRY_ENTRY_ARRAY, entries); - handleRegistryData1_20_5(wrapper.user(), registryKey, entries); - }); + final RegistryDataRewriter registryDataRewriter = registryDataRewriter(); + protocol.registerClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, registryDataRewriter::handle); protocol.registerClientbound(ClientboundPackets1_21.LOGIN, new PacketHandlers() { @Override @@ -233,6 +205,25 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter { + wrapper.passthrough(Types.VAR_INT); // Entity ID + + wrapper.passthrough(Types.DOUBLE); // X + wrapper.passthrough(Types.DOUBLE); // Y + wrapper.passthrough(Types.DOUBLE); // Z + + // Unused... + wrapper.write(Types.DOUBLE, 0D); // Delta movement X + wrapper.write(Types.DOUBLE, 0D); // Delta movement Y + wrapper.write(Types.DOUBLE, 0D); // Delta movement Z + + // Unpack y and x rot + final byte yaw = wrapper.read(Types.BYTE); + final byte pitch = wrapper.read(Types.BYTE); + wrapper.write(Types.FLOAT, yaw * 360F / 256F); + wrapper.write(Types.FLOAT, pitch * 360F / 256F); + }); + protocol.registerServerbound(ServerboundPackets1_21_2.MOVE_PLAYER_POS, wrapper -> { wrapper.passthrough(Types.DOUBLE); // X wrapper.passthrough(Types.DOUBLE); // Y @@ -255,66 +246,25 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter attributesList = TagUtil.getNamespacedCompoundTagList(effects, "attributes"); - if (attributesList == null) { - return; - } - - for (final CompoundTag attributeData : attributesList) { - updateAttributeField(attributeData, mappings); - } - } - - private static void updateLocationChangedAttributes(final CompoundTag effects, final FullMappings mappings) { - final ListTag locationChanged = TagUtil.getNamespacedCompoundTagList(effects, "location_changed"); - if (locationChanged == null) { - return; - } - - for (final CompoundTag data : locationChanged) { - final CompoundTag effect = data.getCompoundTag("effect"); - if (effect == null) { - continue; - } - - updateAttributeField(effect, mappings); - - final ListTag innerEffects = effect.getListTag("effects", CompoundTag.class); - if (innerEffects != null) { - for (final CompoundTag innerEffect : innerEffects) { - updateAttributeField(innerEffect, mappings); - } - } - } - } - - private static void updateAttributeField(final CompoundTag attributeData, final FullMappings mappings) { - final StringTag attributeTag = attributeData.getStringTag("attribute"); - if (attributeTag == null) { - return; - } - - final String attribute = Key.stripMinecraftNamespace(attributeTag.getValue()); - String mappedAttribute = mappings.mappedIdentifier(attribute); - if (mappedAttribute == null) { - mappedAttribute = mappings.mappedIdentifier(0); // Dummy - } - attributeTag.setValue(mappedAttribute); + final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(protocol); + registryDataRewriter.addEntries( + "damage_type", + new RegistryEntry("minecraft:ender_pearl", enderpearlData), + new RegistryEntry("minecraft:mace_smash", maceSmashData) + ); + registryDataRewriter.addEnchantmentEffectRewriter("damage_item", tag -> tag.putString("type", "change_item_damage")); + return registryDataRewriter; } private void readOnGround(final PacketWrapper wrapper) { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java new file mode 100644 index 000000000..9dc50a1cf --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter.java @@ -0,0 +1,94 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter; + +import com.viaversion.viaversion.api.minecraft.HolderSet; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21; + +final class RecipeRewriter extends RecipeRewriter1_20_3 { + + RecipeRewriter(final Protocol protocol) { + super(protocol); + } + + @Override + protected void handleIngredient(final PacketWrapper wrapper) { + wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); + } + + @Override + public void handleCraftingShaped(final PacketWrapper wrapper) { + wrapper.passthrough(Types.STRING); // Group + wrapper.passthrough(Types.VAR_INT); // Crafting book category + final int width = wrapper.passthrough(Types.VAR_INT); + final int height = wrapper.passthrough(Types.VAR_INT); + final int ingredients = width * height; + + wrapper.write(Types.VAR_INT, ingredients); + for (int i = 0; i < ingredients; i++) { + wrapper.write(Types.HOLDER_SET, ingredient(wrapper)); + } + + wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); // Result + wrapper.passthrough(Types.BOOLEAN); // Show notification + } + + @Override + public void handleCraftingShapeless(final PacketWrapper wrapper) { + wrapper.passthrough(Types.STRING); // Group + wrapper.passthrough(Types.VAR_INT); // Crafting book category + + final int ingredients = wrapper.read(Types.VAR_INT); + final HolderSet[] ingredient = new HolderSet[ingredients]; + for (int i = 0; i < ingredients; i++) { + ingredient[i] = ingredient(wrapper); + } + + wrapper.write(mappedItemType(), rewrite(wrapper.user(), wrapper.read(itemType()))); + + // Also moved below here + wrapper.write(Types.VAR_INT, ingredients); + for (final HolderSet item : ingredient) { + wrapper.write(Types.HOLDER_SET, item); + } + } + + private HolderSet ingredient(final PacketWrapper wrapper) { + final Item[] items = wrapper.read(itemArrayType()); + final int[] ids = new int[items.length]; + for (int i = 0; i < items.length; i++) { + final Item item = rewrite(wrapper.user(), items[i]); + ids[i] = item.identifier(); + } + return HolderSet.of(ids); + } + + @Override + public void handleRecipeType(final PacketWrapper wrapper, final String type) { + if (type.equals("crafting_special_suspiciousstew") || type.equals("crafting_special_shulkerboxcoloring")) { + wrapper.read(Types.VAR_INT); // Crafting book category + } else { + super.handleRecipeType(wrapper, type); + } + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java index eeb5359ee..d58c2b944 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/RecipeRewriter1_21_2.java @@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3; +// Dead as of 1.21.2, the server only sends simple recipe display data instead of full results public class RecipeRewriter1_21_2 extends RecipeRewriter1_20_3 { public RecipeRewriter1_21_2(final Protocol protocol) { diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java index 8704036fe..0e0c7bc9a 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/RecipeRewriter.java @@ -88,6 +88,7 @@ public class RecipeRewriter { int newSize = size; for (int i = 0; i < size; i++) { final String recipeIdentifier = wrapper.read(Types.STRING); + // TODO Recipe identifier, then int array item ids and that's it final FullMappings recipeSerializerMappings = protocol.getMappingData().getRecipeSerializerMappings(); final int typeId = wrapper.read(Types.VAR_INT); @@ -101,6 +102,11 @@ public class RecipeRewriter { handleRecipeType(wrapper, Key.stripMinecraftNamespace(recipeSerializerMappings.identifier(typeId))); // Use the original } + + // Stonecutter recipes on their own + // List of: + // Ingredient (item holder set) + // Slot display id }); } diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/RegistryDataRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/RegistryDataRewriter.java new file mode 100644 index 000000000..0e8ad5482 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/RegistryDataRewriter.java @@ -0,0 +1,195 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.rewriter; + +import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.nbt.tag.ListTag; +import com.viaversion.nbt.tag.StringTag; +import com.viaversion.nbt.tag.Tag; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.data.FullMappings; +import com.viaversion.viaversion.api.data.entity.DimensionData; +import com.viaversion.viaversion.api.minecraft.RegistryEntry; +import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.data.entity.DimensionDataImpl; +import com.viaversion.viaversion.util.Key; +import com.viaversion.viaversion.util.TagUtil; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class RegistryDataRewriter { + private final Map> enchantmentEffectRewriters = new Object2ObjectArrayMap<>(); + private final Map> toAdd = new Object2ObjectArrayMap<>(); + private final Protocol protocol; + + public RegistryDataRewriter(Protocol protocol) { + this.protocol = protocol; + } + + public void handle(final PacketWrapper wrapper) { + final String registryKey = wrapper.passthrough(Types.STRING); + RegistryEntry[] entries = wrapper.read(Types.REGISTRY_ENTRY_ARRAY); + entries = handle(wrapper.user(), registryKey, entries); + wrapper.write(Types.REGISTRY_ENTRY_ARRAY, entries); + } + + public RegistryEntry[] handle(final UserConnection connection, String key, RegistryEntry[] entries) { + key = Key.stripMinecraftNamespace(key); + if (key.equals("enchantment")) { + updateEnchantments(entries); + } + + final List toAdd = this.toAdd.get(key); + if (toAdd != null) { + final int length = entries.length; + final int toAddLength = toAdd.size(); + entries = Arrays.copyOf(entries, length + toAddLength); + for (int i = 0; i < toAddLength; i++) { + entries[length + i] = toAdd.get(i).copy(); + } + } + + trackDimensionAndBiomes(connection, key, entries); + return entries; + } + + public void addEntries(final String registryKey, final RegistryEntry... entries) { + toAdd.computeIfAbsent(Key.stripMinecraftNamespace(registryKey), $ -> new ArrayList<>()).addAll(List.of(entries)); + } + + public void addEnchantmentEffectRewriter(final String key, final Consumer rewriter) { + enchantmentEffectRewriters.put(Key.stripMinecraftNamespace(key), rewriter); + } + + public void trackDimensionAndBiomes(final UserConnection connection, final String registryKey, final RegistryEntry[] entries) { + if (registryKey.equals("worldgen/biome")) { + protocol.getEntityRewriter().tracker(connection).setBiomesSent(entries.length); + } else if (registryKey.equals("dimension_type")) { + final Map dimensionDataMap = new HashMap<>(entries.length); + for (int i = 0; i < entries.length; i++) { + final RegistryEntry entry = entries[i]; + final String key = Key.stripMinecraftNamespace(entry.key()); + final DimensionData dimensionData = entry.tag() != null + ? new DimensionDataImpl(i, (CompoundTag) entry.tag()) + : DimensionDataImpl.withDefaultsFor(key, i); + dimensionDataMap.put(key, dimensionData); + } + protocol.getEntityRewriter().tracker(connection).setDimensions(dimensionDataMap); + } + } + + public void updateEnchantments(final RegistryEntry[] entries) { + for (final RegistryEntry entry : entries) { + if (entry.tag() == null) { + continue; + } + + final CompoundTag effects = ((CompoundTag) entry.tag()).getCompoundTag("effects"); + if (effects == null) { + continue; + } + + // Go through all effects, almost all of them may contain an "effect" element + for (final Map.Entry effectEntry : effects.entrySet()) { + if (effectEntry.getValue() instanceof final CompoundTag compoundTag) { + updateNestedEffect(compoundTag); + } else if (effectEntry.getValue() instanceof final ListTag listTag && listTag.getElementType() == CompoundTag.class) { + for (final Tag tag : listTag) { + updateNestedEffect((CompoundTag) tag); + } + } + } + + updateAttributesFields(effects); + } + } + + private void updateNestedEffect(final CompoundTag effectsTag) { + final CompoundTag effect = effectsTag.getCompoundTag("effect"); + if (effect == null) { + return; + } + + runEffectRewriters(effect); + + final ListTag innerEffects = effect.getListTag("effects", CompoundTag.class); + if (innerEffects == null) { + return; + } + + for (final CompoundTag innerEffect : innerEffects) { + runEffectRewriters(innerEffect); + } + } + + private void updateAttributesFields(final CompoundTag effects) { + if (!hasAttributeMappings()) { + return; + } + + final ListTag attributesList = TagUtil.getNamespacedCompoundTagList(effects, "attributes"); + if (attributesList == null) { + return; + } + + for (final CompoundTag attributeData : attributesList) { + updateAttributeField(attributeData); + } + } + + private void runEffectRewriters(final CompoundTag effectTag) { + String effect = effectTag.getString("type"); + if (effect == null) { + return; + } + + effect = Key.stripMinecraftNamespace(effect); + updateAttributeField(effectTag); + + final Consumer rewriter = enchantmentEffectRewriters.get(effect); + if (rewriter != null) { + rewriter.accept(effectTag); + } + } + + private void updateAttributeField(final CompoundTag attributeData) { + final StringTag attributeTag = attributeData.getStringTag("attribute"); + if (attributeTag == null) { + return; + } + + final FullMappings mappings = protocol.getMappingData().getAttributeMappings(); + final String attribute = Key.stripMinecraftNamespace(attributeTag.getValue()); + String mappedAttribute = mappings.mappedIdentifier(attribute); + if (mappedAttribute == null) { + mappedAttribute = mappings.mappedIdentifier(0); // Dummy + } + attributeTag.setValue(mappedAttribute); + } + + private boolean hasAttributeMappings() { + return protocol.getMappingData() != null && protocol.getMappingData().getAttributeMappings() != null; + } +} diff --git a/common/src/main/resources/assets/viaversion/data/identifier-table.nbt b/common/src/main/resources/assets/viaversion/data/identifier-table.nbt index 226f11af6..f36b63aea 100644 Binary files a/common/src/main/resources/assets/viaversion/data/identifier-table.nbt and b/common/src/main/resources/assets/viaversion/data/identifier-table.nbt differ diff --git a/common/src/main/resources/assets/viaversion/data/identifiers-1.21.2.nbt b/common/src/main/resources/assets/viaversion/data/identifiers-1.21.2.nbt index 26a8eed17..bf1533582 100644 Binary files a/common/src/main/resources/assets/viaversion/data/identifiers-1.21.2.nbt and b/common/src/main/resources/assets/viaversion/data/identifiers-1.21.2.nbt differ diff --git a/common/src/main/resources/assets/viaversion/data/mappings-1.21to1.21.2.nbt b/common/src/main/resources/assets/viaversion/data/mappings-1.21to1.21.2.nbt index 0ce1950ad..67a3b2a58 100644 Binary files a/common/src/main/resources/assets/viaversion/data/mappings-1.21to1.21.2.nbt and b/common/src/main/resources/assets/viaversion/data/mappings-1.21to1.21.2.nbt differ