diff --git a/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java b/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java index 584928e65..441f050a7 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/recipe/TrimRecipe.java @@ -25,63 +25,68 @@ package org.geysermc.geyser.inventory.recipe; +import com.github.steveice10.mc.protocol.data.game.RegistryEntry; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; import org.cloudburstmc.protocol.bedrock.data.TrimPattern; import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount; import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescriptor; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.text.MessageTranslator; /** - * Hardcoded recipe information about armor trims until further improvements can be made. This information was scraped - * from BDS 1.19.81 with a world with the next_major_update and sniffer features enabled, using ProxyPass. + * Stores information on trim materials and patterns, including smithing armor hacks for pre-1.20. */ -public class TrimRecipe { - - // For TrimDataPacket, which BDS sends just before the CraftingDataPacket - public static final List PATTERNS; - public static final List MATERIALS; - +public final class TrimRecipe { // For CraftingDataPacket public static final String ID = "minecraft:smithing_armor_trim"; public static final ItemDescriptorWithCount BASE = tagDescriptor("minecraft:trimmable_armors"); public static final ItemDescriptorWithCount ADDITION = tagDescriptor("minecraft:trim_materials"); public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates"); - static { - List patterns = new ArrayList<>(16); - patterns.add(new TrimPattern("minecraft:ward_armor_trim_smithing_template", "ward")); - patterns.add(new TrimPattern("minecraft:sentry_armor_trim_smithing_template", "sentry")); - patterns.add(new TrimPattern("minecraft:snout_armor_trim_smithing_template", "snout")); - patterns.add(new TrimPattern("minecraft:dune_armor_trim_smithing_template", "dune")); - patterns.add(new TrimPattern("minecraft:spire_armor_trim_smithing_template", "spire")); - patterns.add(new TrimPattern("minecraft:tide_armor_trim_smithing_template", "tide")); - patterns.add(new TrimPattern("minecraft:wild_armor_trim_smithing_template", "wild")); - patterns.add(new TrimPattern("minecraft:rib_armor_trim_smithing_template", "rib")); - patterns.add(new TrimPattern("minecraft:coast_armor_trim_smithing_template", "coast")); - patterns.add(new TrimPattern("minecraft:shaper_armor_trim_smithing_template", "shaper")); - patterns.add(new TrimPattern("minecraft:eye_armor_trim_smithing_template", "eye")); - patterns.add(new TrimPattern("minecraft:vex_armor_trim_smithing_template", "vex")); - patterns.add(new TrimPattern("minecraft:silence_armor_trim_smithing_template", "silence")); - patterns.add(new TrimPattern("minecraft:wayfinder_armor_trim_smithing_template", "wayfinder")); - patterns.add(new TrimPattern("minecraft:raiser_armor_trim_smithing_template", "raiser")); - patterns.add(new TrimPattern("minecraft:host_armor_trim_smithing_template", "host")); - PATTERNS = Collections.unmodifiableList(patterns); + public static TrimMaterial readTrimMaterial(GeyserSession session, RegistryEntry entry) { + String key = stripNamespace(entry.getId()); - List materials = new ArrayList<>(10); - materials.add(new TrimMaterial("quartz", "§h", "minecraft:quartz")); - materials.add(new TrimMaterial("iron", "§i", "minecraft:iron_ingot")); - materials.add(new TrimMaterial("netherite", "§j", "minecraft:netherite_ingot")); - materials.add(new TrimMaterial("redstone", "§m", "minecraft:redstone")); - materials.add(new TrimMaterial("copper", "§n", "minecraft:copper_ingot")); - materials.add(new TrimMaterial("gold", "§p", "minecraft:gold_ingot")); - materials.add(new TrimMaterial("emerald", "§q", "minecraft:emerald")); - materials.add(new TrimMaterial("diamond", "§s", "minecraft:diamond")); - materials.add(new TrimMaterial("lapis", "§t", "minecraft:lapis_lazuli")); - materials.add(new TrimMaterial("amethyst", "§u", "minecraft:amethyst_shard")); - MATERIALS = Collections.unmodifiableList(materials); + // Color is used when hovering over the item + // Find the nearest legacy color from the RGB Java gives us to work with + // Also yes this is a COMPLETE hack but it works ok!!!!! + StringTag colorTag = ((CompoundTag) entry.getData().get("description")).get("color"); + TextColor color = TextColor.fromHexString(colorTag.getValue()); + String legacy = MessageTranslator.convertMessage(Component.space().color(color)); + + String itemIdentifier = ((StringTag) entry.getData().get("ingredient")).getValue(); + ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier); + if (itemMapping == null) { + // This should never happen so not sure what to do here. + itemMapping = ItemMapping.AIR; + } + // Just pick out the resulting color code, without RESET in front. + return new TrimMaterial(key, legacy.substring(2).trim(), itemMapping.getBedrockIdentifier()); + } + + public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) { + String key = stripNamespace(entry.getId()); + + String itemIdentifier = ((StringTag) entry.getData().get("template_item")).getValue(); + ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier); + if (itemMapping == null) { + // This should never happen so not sure what to do here. + itemMapping = ItemMapping.AIR; + } + return new TrimPattern(itemMapping.getBedrockIdentifier(), key); + } + + // TODO find a good place for a stripNamespace util method + private static String stripNamespace(String identifier) { + int i = identifier.indexOf(':'); + if (i >= 0) { + return identifier.substring(i + 1); + } + return identifier; } private TrimRecipe() { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java index ab936bd08..773b850c0 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java @@ -33,6 +33,8 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; +import org.cloudburstmc.protocol.bedrock.data.TrimPattern; import org.geysermc.geyser.item.ArmorMaterial; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -57,14 +59,13 @@ public class ArmorItem extends Item { return; } - // TODO material IDs - String material = trim.material().custom().assetName(); - String pattern = trim.pattern().custom().assetId(); + TrimMaterial material = session.getRegistryCache().trimMaterials().get(trim.material().id()); + TrimPattern pattern = session.getRegistryCache().trimPatterns().get(trim.pattern().id()); NbtMapBuilder trimBuilder = NbtMap.builder(); // bedrock has an uppercase first letter key, and the value is not namespaced - trimBuilder.put("Material", stripNamespace(material)); - trimBuilder.put("Pattern", stripNamespace(pattern)); + trimBuilder.put("Material", material.getMaterialId()); + trimBuilder.put("Pattern", pattern.getPatternId()); builder.putCompound("Trim", trimBuilder.build()); } } @@ -86,12 +87,4 @@ public class ArmorItem extends Item { public boolean isValidRepairItem(Item other) { return material.getRepairIngredient() == other; } - - private static String stripNamespace(String identifier) { - int i = identifier.indexOf(':'); - if (i >= 0) { - return identifier.substring(i + 1); - } - return identifier; - } } diff --git a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java index 6e7223da0..4babc3af0 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java +++ b/core/src/main/java/org/geysermc/geyser/level/JavaDimension.java @@ -55,4 +55,18 @@ public record JavaDimension(int minY, int maxY, boolean piglinSafe, double world map.put(i, new JavaDimension(minY, maxY, piglinSafe, coordinateScale)); } } + + public static JavaDimension read(RegistryEntry entry) { + CompoundTag dimension = entry.getData(); + int minY = ((IntTag) dimension.get("min_y")).getValue(); + int maxY = ((IntTag) dimension.get("height")).getValue(); + // Logical height can be ignored probably - seems to be for artificial limits like the Nether. + + // Set if piglins/hoglins should shake + boolean piglinSafe = ((Number) dimension.get("piglin_safe").getValue()).byteValue() != (byte) 0; + // Load world coordinate scale for the world border + double coordinateScale = ((Number) dimension.get("coordinate_scale").getValue()).doubleValue(); + + return new JavaDimension(minY, maxY, piglinSafe, coordinateScale); + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 994879f82..9ae1f6f49 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -44,7 +44,6 @@ import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic; import com.github.steveice10.mc.protocol.data.game.statistic.Statistic; import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundClientInformationPacket; import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket; -import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandSignedPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; @@ -54,11 +53,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket; import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.Session; -import com.github.steveice10.packetlib.event.session.ConnectedEvent; -import com.github.steveice10.packetlib.event.session.DisconnectedEvent; -import com.github.steveice10.packetlib.event.session.PacketErrorEvent; -import com.github.steveice10.packetlib.event.session.PacketSendingEvent; -import com.github.steveice10.packetlib.event.session.SessionAdapter; +import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpClientSession; import com.github.steveice10.packetlib.tcp.TcpSession; @@ -66,8 +61,6 @@ import io.netty.channel.Channel; import io.netty.channel.EventLoop; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -146,7 +139,6 @@ import org.geysermc.geyser.session.cache.*; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; -import org.geysermc.geyser.text.TextDecoration; import org.geysermc.geyser.translator.inventory.InventoryTranslator; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.ChunkUtils; @@ -158,16 +150,7 @@ import java.net.ConnectException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; @@ -214,6 +197,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private final LodestoneCache lodestoneCache; private final PistonCache pistonCache; private final PreferencesCache preferencesCache; + private final RegistryCache registryCache; private final SkullCache skullCache; private final StructureBlockCache structureBlockCache; private final TagCache tagCache; @@ -263,12 +247,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @Setter private ItemMappings itemMappings; - /** - * Stores the map between Java and Bedrock biome network IDs. - */ - @Setter - private int[] biomeTranslations = null; - /** * A map of Vector3i positions to Java entities. * Used for translating Bedrock block actions to Java entity actions. @@ -360,12 +338,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { @MonotonicNonNull @Setter private JavaDimension dimensionType = null; - /** - * All dimensions that the client could possibly connect to. - */ - private final Int2ObjectMap dimensions = new Int2ObjectOpenHashMap<>(4); - - private final Int2ObjectMap chatTypes = new Int2ObjectOpenHashMap<>(7); @Setter private int breakingBlock; @@ -619,6 +591,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.lodestoneCache = new LodestoneCache(); this.pistonCache = new PistonCache(this); this.preferencesCache = new PreferencesCache(this); + this.registryCache = new RegistryCache(this); this.skullCache = new SkullCache(this); this.structureBlockCache = new StructureBlockCache(); this.tagCache = new TagCache(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java new file mode 100644 index 000000000..44a5469b8 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019-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.session.cache; + +import com.github.steveice10.mc.protocol.data.game.RegistryEntry; +import com.github.steveice10.mc.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket; +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 lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; +import org.cloudburstmc.protocol.bedrock.data.TrimPattern; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.inventory.recipe.TrimRecipe; +import org.geysermc.geyser.level.JavaDimension; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.TextDecoration; +import org.geysermc.geyser.translator.level.BiomeTranslator; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.ToIntFunction; + +/** + * Stores any information sent via Java registries. May not contain all data in a given registry - we'll strip what's + * unneeded. + * + * Crafted as of 1.20.5 for easy "add new registry" in the future. + */ +@Accessors(fluent = true) +@Getter +public final class RegistryCache { + private static final Map>> REGISTRIES = new HashMap<>(); + + static { + register("chat_type", cache -> cache.chatTypes, ($, entry) -> TextDecoration.readChatType(entry)); + register("dimension_type", cache -> cache.dimensions, ($, entry) -> JavaDimension.read(entry)); + register("trim_material", cache -> cache.trimMaterials, TrimRecipe::readTrimMaterial); + register("trim_pattern", cache -> cache.trimPatterns, TrimRecipe::readTrimPattern); + register("worldgen/biome", (cache, array) -> cache.biomeTranslations = array, BiomeTranslator::loadServerBiome); + } + + @Getter(AccessLevel.NONE) + private final GeyserSession session; + + /** + * Java -> Bedrock biome network IDs. + */ + private int[] biomeTranslations; + private final Int2ObjectMap chatTypes = new Int2ObjectOpenHashMap<>(7); + /** + * All dimensions that the client could possibly connect to. + */ + private final Int2ObjectMap dimensions = new Int2ObjectOpenHashMap<>(4); + private final Int2ObjectMap trimMaterials = new Int2ObjectOpenHashMap<>(); + private final Int2ObjectMap trimPatterns = new Int2ObjectOpenHashMap<>(); + + public RegistryCache(GeyserSession session) { + this.session = session; + } + + /** + * Loads a registry in, if we are tracking it. + */ + public void load(ClientboundRegistryDataPacket packet) { + var reader = REGISTRIES.get(packet.getRegistry()); + if (reader != null) { + reader.accept(this, packet.getEntries()); + } else { + GeyserImpl.getInstance().getLogger().debug("Ignoring registry of type " + packet.getRegistry()); + } + } + + /** + * @param registry the Java registry resource location, without the "minecraft:" prefix. + * @param localCacheFunction which local field in RegistryCache are we caching entries for this registry? + * @param reader converts the RegistryEntry NBT into a class file + * @param the class that represents these entries. + */ + private static void register(String registry, Function> localCacheFunction, BiFunction reader) { + REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> { + Int2ObjectMap localCache = localCacheFunction.apply(registryCache); + // Clear each local cache every time a new registry entry is given to us + localCache.clear(); + for (int i = 0; i < entries.size(); i++) { + RegistryEntry entry = entries.get(i); + // This is what Geyser wants to keep as a value for this registry. + T cacheEntry = reader.apply(registryCache.session, entry); + localCache.put(i, cacheEntry); + } + // Trim registry down to needed size + if (localCache instanceof Int2ObjectOpenHashMap hashMap) { + hashMap.trim(); + } + }); + } + + /** + * @param localCacheFunction the int array to set the final values to. + */ + private static void register(String registry, BiConsumer localCacheFunction, ToIntFunction reader) { + REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> { + Int2IntMap temp = new Int2IntOpenHashMap(); + int greatestId = 0; + for (int i = 0; i < entries.size(); i++) { + RegistryEntry entry = entries.get(i); + // This is what Geyser wants to keep as a value for this registry. + int cacheEntry = reader.applyAsInt(entry); + temp.put(i, cacheEntry); + if (i > greatestId) { + // Maximum registry ID, so far. Make sure the final array is at least this large. + greatestId = i; + } + } + + int[] array = new int[greatestId + 1]; + for (Int2IntMap.Entry entry : temp.int2IntEntrySet()) { + array[entry.getIntKey()] = entry.getIntValue(); + } + localCacheFunction.accept(registryCache, array); + }); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java b/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java index 121e1b2b9..bad55a5ca 100644 --- a/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java +++ b/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.text; +import com.github.steveice10.mc.protocol.data.game.RegistryEntry; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; @@ -87,6 +88,17 @@ public final class TextDecoration { '}'; } + public static TextDecoration readChatType(RegistryEntry entry) { + // Note: The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla. + CompoundTag tag = entry.getData(); + CompoundTag chat = tag.get("chat"); + TextDecoration textDecoration = null; + if (chat != null) { + textDecoration = new TextDecoration(chat); + } + return textDecoration; + } + public enum Parameter { CONTENT, SENDER, diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java index f05ed5ed1..fce928bdb 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/BiomeTranslator.java @@ -31,7 +31,9 @@ import com.github.steveice10.mc.protocol.data.game.chunk.DataPalette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.GlobalPalette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.Palette; import com.github.steveice10.mc.protocol.data.game.chunk.palette.SingletonPalette; -import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntLists; import org.geysermc.geyser.level.chunk.BlockStorage; import org.geysermc.geyser.level.chunk.bitarray.BitArray; import org.geysermc.geyser.level.chunk.bitarray.BitArrayVersion; @@ -39,58 +41,20 @@ import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.GeyserSession; -import java.util.List; - // Array index formula by https://wiki.vg/Chunk_Format public class BiomeTranslator { - public static void loadServerBiomes(GeyserSession session, List entries) { - Int2IntMap biomeTranslations = new Int2IntOpenHashMap(); - - int greatestBiomeId = 0; - for (int i = 0; i < entries.size(); i++) { - RegistryEntry entry = entries.get(i); - String javaIdentifier = entry.getId(); - int bedrockId = Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, 0); - int javaId = i; - if (javaId > greatestBiomeId) { - greatestBiomeId = javaId; - } - - // TODO - the category tag no longer exists - find a better replacement option -// if (bedrockId == -1) { -// // There is no matching Bedrock variation for this biome; let's set the closest match based on biome category -// String category = ((StringTag) ((CompoundTag) biomeTag.get("element")).get("category")).getValue(); -// String replacementBiome = switch (category) { -// case "extreme_hills" -> "minecraft:mountains"; -// case "icy" -> "minecraft:ice_spikes"; -// case "mesa" -> "minecraft:badlands"; -// case "mushroom" -> "minecraft:mushroom_fields"; -// case "nether" -> "minecraft:nether_wastes"; -// default -> "minecraft:ocean"; // Typically ID 0 so a good default -// case "taiga", "jungle", "plains", "savanna", "the_end", "beach", "ocean", "desert", "river", "swamp" -> "minecraft:" + category; -// }; -// bedrockId = Registries.BIOME_IDENTIFIERS.get().getInt(replacementBiome); -// } - - // When we see the Java ID, we should instead apply the Bedrock ID - biomeTranslations.put(javaId, bedrockId); - - if (javaId == 0) { - // Matches Java behavior when it sees an invalid biome - it just replaces it with ID 0 - biomeTranslations.defaultReturnValue(bedrockId); - } - } - - int[] biomes = new int[greatestBiomeId + 1]; - for (Int2IntMap.Entry entry : biomeTranslations.int2IntEntrySet()) { - biomes[entry.getIntKey()] = entry.getIntValue(); - } - session.setBiomeTranslations(biomes); + public static int loadServerBiome(RegistryEntry entry) { + String javaIdentifier = entry.getId(); + return Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, 0); +// if (javaId == 0) { +// // Matches Java behavior when it sees an invalid biome - it just replaces it with ID 0 +// biomeTranslations.defaultReturnValue(bedrockId); +// } } public static BlockStorage toNewBedrockBiome(GeyserSession session, DataPalette biomeData) { - int[] biomeTranslations = session.getBiomeTranslations(); + int[] biomeTranslations = session.getRegistryCache().biomeTranslations(); // As of 1.17.10: the client expects the same format as a chunk but filled with biomes // As of 1.18 this is the same as Java Edition diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRegistryDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRegistryDataTranslator.java index 5d9bbff7d..ddb9c76b7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRegistryDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRegistryDataTranslator.java @@ -25,49 +25,16 @@ package org.geysermc.geyser.translator.protocol.java; -import com.github.steveice10.mc.protocol.data.game.RegistryEntry; import com.github.steveice10.mc.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.TextDecoration; -import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; -import java.util.List; - @Translator(packet = ClientboundRegistryDataPacket.class) public class JavaRegistryDataTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundRegistryDataPacket packet) { - if (packet.getRegistry().equals("minecraft:dimension_type")) { - Int2ObjectMap dimensions = session.getDimensions(); - dimensions.clear(); - JavaDimension.load(packet.getEntries(), dimensions); - } - - if (packet.getRegistry().equals("minecraft:chat_type")) { - Int2ObjectMap chatTypes = session.getChatTypes(); - chatTypes.clear(); - List entries = packet.getEntries(); - for (int i = 0; i < entries.size(); i++) { - // The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla. - RegistryEntry entry = entries.get(i); - CompoundTag tag = entry.getData(); - CompoundTag chat = tag.get("chat"); - TextDecoration textDecoration = null; - if (chat != null) { - textDecoration = new TextDecoration(chat); - } - chatTypes.put(i, textDecoration); - } - } - - if (packet.getRegistry().equals("minecraft:worldgen/biome")) { - BiomeTranslator.loadServerBiomes(session, packet.getEntries()); - } + session.getRegistryCache().load(packet); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java index aca02feab..20b223248 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaUpdateRecipesTranslator.java @@ -269,8 +269,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator