diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java index dc4a2030d..fb2c105eb 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java @@ -46,9 +46,8 @@ public interface GeyserInstrument { NbtMap data = context.data(); String soundEvent = SoundUtils.readSoundEvent(data, "instrument " + context.id()); float range = data.getFloat("range"); - String description = MessageTranslator.deserializeDescriptionForTooltip(context.session(), data); BedrockInstrument bedrockInstrument = BedrockInstrument.getByJavaIdentifier(context.id()); - return new GeyserInstrument.Impl(soundEvent, range, description, bedrockInstrument); + return new GeyserInstrument.Impl(soundEvent, range, context.deserializeDescription(), bedrockInstrument); } String soundEvent(); 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 9b49f8577..e4ddffd34 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 @@ -68,16 +68,18 @@ public final class TrimRecipe { int networkId = context.getNetworkId(context.id()); ItemMapping trimItem = null; - for (ProvidesTrimMaterial provider : materialProviders().keySet()) { - Holder materialHolder = provider.materialHolder(); - if (context.id().equals(provider.materialLocation()) || (materialHolder != null && materialHolder.isId() && materialHolder.id() == networkId)) { - trimItem = context.session().getItemMappings().getMapping(materialProviders().get(provider)); - break; + if (context.session().isPresent()) { + for (ProvidesTrimMaterial provider : materialProviders().keySet()) { + Holder materialHolder = provider.materialHolder(); + if (context.id().equals(provider.materialLocation()) || (materialHolder != null && materialHolder.isId() && materialHolder.id() == networkId)) { + trimItem = context.session().get().getItemMappings().getMapping(materialProviders().get(provider)); + break; + } } } if (trimItem == null) { - // This happens for custom trim materials, not sure what to do here. + // This happens in testing and for custom trim materials, not sure what to do for the latter. GeyserImpl.getInstance().getLogger().debug("Unable to found trim material item for material " + context.id()); trimItem = ItemMapping.AIR; } @@ -91,10 +93,14 @@ public final class TrimRecipe { // Not ideal, Java edition also gives us a translatable description... Bedrock wants the template item String identifier = context.id().asString() + "_armor_trim_smithing_template"; - ItemMapping itemMapping = context.session().getItemMappings().getMapping(identifier); - if (itemMapping == null) { - // This should never happen so not sure what to do here. - itemMapping = ItemMapping.AIR; + ItemMapping itemMapping = ItemMapping.AIR; + if (context.session().isPresent()) { + itemMapping = context.session().get().getItemMappings().getMapping(identifier); + if (itemMapping == null) { + // This should never happen so not sure what to do here. + GeyserImpl.getInstance().getLogger().debug("Unable to found trim pattern item for pattern " + context.id()); + itemMapping = ItemMapping.AIR; + } } return new TrimPattern(itemMapping.getBedrockIdentifier(), key); } diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 8ad58c9c3..999777a00 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -32,7 +32,6 @@ import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.tags.GeyserHolderSet; -import org.geysermc.geyser.translator.text.MessageTranslator; import java.util.HashSet; import java.util.Map; @@ -54,7 +53,9 @@ public record Enchantment(Set effects, NbtMap data = context.data(); Set effects = readEnchantmentComponents(data.getCompound("effects")); - GeyserHolderSet supportedItems = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ITEM, data.get("supported_items")); + GeyserHolderSet supportedItems = context.session() + .map(session -> GeyserHolderSet.readHolderSet(session, JavaRegistries.ITEM, data.get("supported_items"))) + .orElseGet(() -> GeyserHolderSet.empty(JavaRegistries.ITEM)); int maxLevel = data.getInt("max_level"); int anvilCost = data.getInt("anvil_cost"); @@ -63,7 +64,7 @@ public record Enchantment(Set effects, BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(context.id().asString()); - String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null; + String description = bedrockEnchantment == null ? context.deserializeDescription() : null; return new Enchantment(effects, supportedItems, maxLevel, description, anvilCost, exclusiveSet, bedrockEnchantment); } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java index ca9840994..d45ea5ffc 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java @@ -216,13 +216,11 @@ public class DataComponentHashers { register(DataComponentTypes.DEBUG_STICK_STATE, MinecraftHasher.NBT_MAP); registerMap(DataComponentTypes.ENTITY_DATA, builder -> builder .accept("id", RegistryHasher.ENTITY_TYPE_KEY, TypedEntityData::type) - .inlineNbt(TypedEntityData::tag) - ); + .accept(TypedEntityData::tag, MapBuilder.inlineNbtMap())); register(DataComponentTypes.BUCKET_ENTITY_DATA, MinecraftHasher.NBT_MAP); registerMap(DataComponentTypes.BLOCK_ENTITY_DATA, builder -> builder .accept("id", RegistryHasher.BLOCK_ENTITY_TYPE_KEY, TypedEntityData::type) - .inlineNbt(TypedEntityData::tag) - ); + .accept(TypedEntityData::tag, MapBuilder.inlineNbtMap())); register(DataComponentTypes.INSTRUMENT, RegistryHasher.INSTRUMENT_COMPONENT); register(DataComponentTypes.PROVIDES_TRIM_MATERIAL, RegistryHasher.PROVIDES_TRIM_MATERIAL); @@ -270,7 +268,7 @@ public class DataComponentHashers { register(DataComponentTypes.PIG_VARIANT, RegistryHasher.PIG_VARIANT); register(DataComponentTypes.COW_VARIANT, RegistryHasher.COW_VARIANT); register(DataComponentTypes.CHICKEN_VARIANT, MinecraftHasher.KEY - .sessionCast((session, holder) -> holder.getOrCompute(id -> JavaRegistries.CHICKEN_VARIANT.key(session, id)))); // Why, Mojang? + .registryCast((session, holder) -> holder.getOrCompute(id -> JavaRegistries.CHICKEN_VARIANT.key(session, id)))); // Why, Mojang? register(DataComponentTypes.FROG_VARIANT, RegistryHasher.FROG_VARIANT); register(DataComponentTypes.HORSE_VARIANT, RegistryHasher.HORSE_VARIANT); register(DataComponentTypes.PAINTING_VARIANT, RegistryHasher.PAINTING_VARIANT.cast(Holder::id)); // This can and will throw when a direct holder was received, which is still possible due to a bug in 1.21.6. @@ -311,7 +309,7 @@ public class DataComponentHashers { public static HashCode hash(GeyserSession session, DataComponentType component, T value) { try { - return hasher(component).hash(value, new MinecraftHashEncoder(session)); + return hasher(component).hash(value, new MinecraftHashEncoder(session.getRegistryCache())); } catch (Exception exception) { GeyserImpl.getInstance().getLogger().error("Failed to hash item data component " + component.getKey() + " with value " + value + "!"); GeyserImpl.getInstance().getLogger().error("This is a Geyser bug, please report this!"); diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MapBuilder.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MapBuilder.java index b6c0c3084..84d180ff6 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MapBuilder.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MapBuilder.java @@ -25,6 +25,10 @@ package org.geysermc.geyser.item.hashing; +import org.cloudburstmc.nbt.NbtList; +import org.cloudburstmc.nbt.NbtMap; + +import java.util.function.Function; import java.util.function.UnaryOperator; /** @@ -52,4 +56,37 @@ public interface MapBuilder extends UnaryOperator> { static MapBuilder unit() { return builder -> builder; } + + /** + * Returns a function that creates a map builder from an NBT map. The builder simply adds all keys from the NBT map. + * + *

Can be used with {@link MapHasher#accept(Function, Function)} to inline an NBT map into a map builder, together with other keys.

+ * + * @param the type to encode. + */ + static Function> inlineNbtMap() { + return map -> builder -> { + for (String key : map.keySet()) { + Object value = map.get(key); + if (value instanceof NbtList list) { + builder.acceptConstant(key, MinecraftHasher.NBT_LIST, list); + } else { + map.listenForByte(key, b -> builder.acceptConstant(key, MinecraftHasher.BYTE, b)); + map.listenForShort(key, s -> builder.acceptConstant(key, MinecraftHasher.SHORT, s)); + map.listenForInt(key, i -> builder.acceptConstant(key, MinecraftHasher.INT, i)); + map.listenForLong(key, l -> builder.acceptConstant(key, MinecraftHasher.LONG, l)); + map.listenForFloat(key, f -> builder.acceptConstant(key, MinecraftHasher.FLOAT, f)); + map.listenForDouble(key, d -> builder.acceptConstant(key, MinecraftHasher.DOUBLE, d)); + map.listenForString(key, s -> builder.acceptConstant(key, MinecraftHasher.STRING, s)); + + map.listenForCompound(key, compound -> builder.acceptConstant(key, MinecraftHasher.NBT_MAP, compound)); + + map.listenForByteArray(key, bytes -> builder.acceptConstant(key, MinecraftHasher.BYTE_ARRAY, bytes)); + map.listenForIntArray(key, ints -> builder.acceptConstant(key, MinecraftHasher.INT_ARRAY, ints)); + map.listenForLongArray(key, longs -> builder.acceptConstant(key, MinecraftHasher.LONG_ARRAY, longs)); + } + } + return builder; + }; + } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java index 187360308..2072ace29 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MapHasher.java @@ -26,8 +26,6 @@ package org.geysermc.geyser.item.hashing; import com.google.common.hash.HashCode; -import org.cloudburstmc.nbt.NbtList; -import org.cloudburstmc.nbt.NbtMap; import java.util.HashMap; import java.util.List; @@ -69,14 +67,6 @@ public class MapHasher { return this; } - private MapHasher accept(String key, Object value, HashCode valueHash) { - if (unhashed != null) { - unhashed.put(key, value); - } - map.put(encoder.string(key), valueHash); - return this; - } - /** * Adds a constant {@link Value} to the map. * @@ -92,25 +82,6 @@ public class MapHasher { return accept(key, hasher.hash(value, encoder)); } - public MapHasher inlineNbt(Function extractor) { - NbtMap nbtMap = extractor.apply(object); - for (String key : nbtMap.keySet()) { - Object value = nbtMap.get(key); - if (value instanceof NbtList list) { - accept(key, value, encoder.nbtList(list)); - } else { - nbtMap.listenForNumber(key, n -> accept(key, value, encoder.number(n))); - nbtMap.listenForString(key, s -> accept(key, value, encoder.string(s))); - nbtMap.listenForCompound(key, compound -> accept(key, value, encoder.nbtMap(compound))); - - nbtMap.listenForByteArray(key, bytes -> accept(key, value, encoder.byteArray(bytes))); - nbtMap.listenForIntArray(key, ints -> accept(key, value, encoder.intArray(ints))); - nbtMap.listenForLongArray(key, longs -> accept(key, value, encoder.longArray(longs))); - } - } - return this; - } - /** * Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map. * diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java index 46f523e09..92b7e3c9e 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java @@ -32,7 +32,7 @@ import com.google.common.hash.Hashing; import org.cloudburstmc.nbt.NbtList; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; -import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; import java.util.ArrayList; import java.util.Comparator; @@ -77,16 +77,16 @@ public class MinecraftHashEncoder { private static final byte[] TRUE = new byte[]{TAG_BOOLEAN, 1}; private final HashFunction hasher; - private final GeyserSession session; + private final JavaRegistryProvider registries; private final HashCode empty; private final HashCode emptyMap; private final HashCode falseHash; private final HashCode trueHash; - public MinecraftHashEncoder(GeyserSession session) { + public MinecraftHashEncoder(JavaRegistryProvider registries) { hasher = Hashing.crc32c(); - this.session = session; + this.registries = registries; empty = hasher.hashBytes(EMPTY); emptyMap = hasher.hashBytes(EMPTY_MAP); @@ -94,8 +94,8 @@ public class MinecraftHashEncoder { trueHash = hasher.hashBytes(TRUE); } - public GeyserSession session() { - return session; + public JavaRegistryProvider registries() { + return registries; } public HashCode empty() { diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java index 7f135e2d6..b23f8dba3 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java @@ -33,7 +33,7 @@ import org.cloudburstmc.nbt.NbtList; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.inventory.item.DyeColor; import org.geysermc.geyser.item.components.Rarity; -import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; import org.geysermc.mcprotocollib.auth.GameProfile; import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos; @@ -52,7 +52,6 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.IntStream; /** * Encodes an object into a {@link HashCode} using a {@link MinecraftHashEncoder}. @@ -66,7 +65,7 @@ import java.util.stream.IntStream; * *
    *
  • {@link MinecraftHasher#list()} creates a hasher that hashes a list of the objects.
  • - *
  • {@link MinecraftHasher#cast(Function)} and {@link MinecraftHasher#sessionCast(BiFunction)} create a new hasher that delegates to this hasher with a converter function.
  • + *
  • {@link MinecraftHasher#cast(Function)} and {@link MinecraftHasher#registryCast(BiFunction)} create a new hasher that delegates to this hasher with a converter function.
  • *
  • {@link MinecraftHasher#filterable()} creates a hasher that hashes a {@link Filterable} instance of the object.
  • *
* @@ -108,13 +107,17 @@ public interface MinecraftHasher { MinecraftHasher BOOL = (b, encoder) -> encoder.bool(b); - MinecraftHasher INT_ARRAY = (ints, encoder) -> encoder.intArray(ints.toArray()); + MinecraftHasher BYTE_ARRAY = (ints, encoder) -> encoder.byteArray(ints); + + MinecraftHasher INT_ARRAY = (ints, encoder) -> encoder.intArray(ints); + + MinecraftHasher LONG_ARRAY = (ints, encoder) -> encoder.longArray(ints); MinecraftHasher NBT_MAP = (map, encoder) -> encoder.nbtMap(map); MinecraftHasher> NBT_LIST = (list, encoder) -> encoder.nbtList(list); - MinecraftHasher POS = INT_ARRAY.cast(pos -> IntStream.of(pos.getX(), pos.getY(), pos.getZ())); + MinecraftHasher POS = INT_ARRAY.cast(pos -> new int[]{pos.getX(), pos.getY(), pos.getZ()}); MinecraftHasher KEY = STRING.cast(Key::asString); @@ -125,7 +128,7 @@ public interface MinecraftHasher { MinecraftHasher UUID = INT_ARRAY.cast(uuid -> { long mostSignificant = uuid.getMostSignificantBits(); long leastSignificant = uuid.getLeastSignificantBits(); - return IntStream.of((int) (mostSignificant >> 32), (int) mostSignificant, (int) (leastSignificant >> 32), (int) leastSignificant); + return new int[]{(int) (mostSignificant >> 32), (int) mostSignificant, (int) (leastSignificant >> 32), (int) leastSignificant}; }); // TODO test MinecraftHasher GAME_PROFILE_PROPERTY = mapBuilder(builder -> builder @@ -188,25 +191,25 @@ public interface MinecraftHasher { /** * "Casts" this hasher to another hash a different object, with a converter method. The returned hasher takes a {@link Casted}, converts it to a {@link Type} using the {@code converter}, and then hashes it. * - *

If a {@link GeyserSession} object is needed for conversion, use {@link MinecraftHasher#sessionCast(BiFunction)}.

+ *

If a {@link JavaRegistryProvider} object is needed for conversion, use {@link MinecraftHasher#registryCast(BiFunction)}.

* * @param converter the converter function that converts a {@link Casted} into a {@link Type}. * @param the type of the new hasher. - * @see MinecraftHasher#sessionCast(BiFunction) + * @see MinecraftHasher#registryCast(BiFunction) */ default MinecraftHasher cast(Function converter) { return (value, encoder) -> hash(converter.apply(value), encoder); } /** - * Like {@link MinecraftHasher#cast(Function)}, but has access to {@link GeyserSession}. + * Like {@link MinecraftHasher#cast(Function)}, but has access to {@link JavaRegistryProvider}. * * @param converter the converter function. * @param the type of the new hasher. * @see MinecraftHasher#cast(Function) */ - default MinecraftHasher sessionCast(BiFunction converter) { - return (value, encoder) -> hash(converter.apply(encoder.session(), value), encoder); + default MinecraftHasher registryCast(BiFunction converter) { + return (value, encoder) -> hash(converter.apply(encoder.registries(), value), encoder); } /** diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java index 85916592c..6b7d0bb21 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java @@ -351,7 +351,7 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher BEEHIVE_OCCUPANT = MinecraftHasher.mapBuilder(builder -> builder .accept("id", RegistryHasher.ENTITY_TYPE_KEY, beehiveOccupant -> beehiveOccupant.getEntityData().type()) - .inlineNbt(beehiveOccupant -> beehiveOccupant.getEntityData().tag()) + .accept(beehiveOccupant -> beehiveOccupant.getEntityData().tag(), MapBuilder.inlineNbtMap()) .accept("ticks_in_hive", INT, BeehiveOccupant::getTicksInHive) .accept("min_ticks_in_hive", INT, BeehiveOccupant::getMinTicksInHive)); @@ -361,7 +361,7 @@ public interface RegistryHasher extends MinecraftHasher { * @param registry the registry to create a hasher for. */ static RegistryHasher registry(JavaRegistryKey registry) { - MinecraftHasher hasher = KEY.sessionCast(registry::key); + MinecraftHasher hasher = KEY.registryCast(registry::key); return hasher::hash; } diff --git a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java index 1bed4099a..b3791dd51 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java +++ b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java @@ -27,7 +27,6 @@ package org.geysermc.geyser.level; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; -import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.SoundUtils; public record JukeboxSong(String soundEvent, String description) { @@ -35,7 +34,6 @@ public record JukeboxSong(String soundEvent, String description) { public static JukeboxSong read(RegistryEntryContext context) { NbtMap data = context.data(); String soundEvent = SoundUtils.readSoundEvent(data, "jukebox song " + context.id()); - String description = MessageTranslator.deserializeDescription(context.session(), data); - return new JukeboxSong(soundEvent, description); + return new JukeboxSong(soundEvent, context.deserializeDescription()); } } 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 index 6e5509600..22f2f3e16 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -49,6 +49,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.JavaRegistry; import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; +import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.RegistryEntryData; import org.geysermc.geyser.session.cache.registry.RegistryUnit; @@ -60,21 +61,24 @@ import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry; import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket; +import org.jetbrains.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; /** * 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" functionality in the future. + *

Crafted as of 1.20.5 for easy "add new registry" functionality in the future.

*/ -public final class RegistryCache { +public final class RegistryCache implements JavaRegistryProvider { private static final Map, Map> DEFAULTS; - private static final Map, RegistryLoader> READERS = new HashMap<>(); + @VisibleForTesting + public static final Map, RegistryReader> READERS = new HashMap<>(); static { register(JavaRegistries.CHAT_TYPE, ChatDecoration::readChatType); @@ -118,7 +122,7 @@ public final class RegistryCache { } private final GeyserSession session; - private final Reference2ObjectMap, JavaRegistry> registries; + private final Reference2ObjectMap, SimpleJavaRegistry> registries; public RegistryCache(GeyserSession session) { this.session = session; @@ -132,13 +136,13 @@ public final class RegistryCache { * Loads a registry in, if we are tracking it. */ public void load(ClientboundRegistryDataPacket packet) { - JavaRegistryKey registryKey = JavaRegistries.fromKey(packet.getRegistry()); + // Java generic mess - we're sure we're putting the current readers for the correct registry types in the READERS map, so we use raw objects here to let it compile + JavaRegistryKey registryKey = JavaRegistries.fromKey(packet.getRegistry()); if (registryKey != null) { - // Java generic mess - we're sure we're putting the current readers for the correct registry types in the READERS map, so we use raw objects here to let it compile - RegistryLoader reader = READERS.get(registryKey); + RegistryReader reader = READERS.get(registryKey); if (reader != null) { try { - reader.load(session, registries.get(registryKey), packet.getEntries()); + readRegistry(session, registryKey, registries.get(registryKey), reader, packet.getEntries()); } catch (Exception exception) { GeyserImpl.getInstance().getLogger().error("Failed parsing registry entries for " + registryKey + "!", exception); } @@ -150,6 +154,7 @@ public final class RegistryCache { } } + @Override public JavaRegistry registry(JavaRegistryKey registryKey) { if (!registries.containsKey(registryKey)) { throw new IllegalArgumentException("The given registry is not data-driven"); @@ -157,49 +162,51 @@ public final class RegistryCache { return (JavaRegistry) registries.get(registryKey); } + private static void readRegistry(GeyserSession session, JavaRegistryKey registryKey, SimpleJavaRegistry registry, + RegistryReader reader, List entries) { + Map localRegistry = null; + + // Clear each local cache every time a new registry entry is given to us + // (e.g. proxy server switches, reconfiguring) + + // Store each of the entries resource location IDs and their respective network ID, used for the key -> ID map in RegistryEntryContext + Object2IntMap entryIdMap = new Object2IntOpenHashMap<>(); + for (int i = 0; i < entries.size(); i++) { + entryIdMap.put(entries.get(i).getId(), i); + } + + List> builder = new ArrayList<>(entries.size()); + for (int i = 0; i < entries.size(); i++) { + RegistryEntry entry = entries.get(i); + // If the data is null, that's the server telling us we need to use our default values. + if (entry.getData() == null) { + if (localRegistry == null) { // Lazy initialize + localRegistry = DEFAULTS.get(registryKey); + } + entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId())); + } + + RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, Optional.of(session)); + // This is what Geyser wants to keep as a value for this registry. + T cacheEntry = reader.read(context); + if (cacheEntry == null) { + // Registry readers should never return null, rather return a default value + throw new IllegalStateException("Registry reader returned null for an entry!"); + } + builder.add(i, new RegistryEntryData<>(i, entry.getId(), cacheEntry)); + } + registry.reset(builder); + } + /** * @param registryKey the Java registry key, listed in {@link JavaRegistries} * @param reader converts the RegistryEntry NBT into an object. Should never return null, rather return a default value! * @param the class that represents these entries. */ private static void register(JavaRegistryKey registryKey, RegistryReader reader) { - register(registryKey, (session, registry, entries) -> { - Map localRegistry = null; - - // Clear each local cache every time a new registry entry is given to us - // (e.g. proxy server switches, reconfiguring) - - // Store each of the entries resource location IDs and their respective network ID, used for the key -> ID map in RegistryEntryContext - Object2IntMap entryIdMap = new Object2IntOpenHashMap<>(); - for (int i = 0; i < entries.size(); i++) { - entryIdMap.put(entries.get(i).getId(), i); - } - - List> builder = new ArrayList<>(entries.size()); - for (int i = 0; i < entries.size(); i++) { - RegistryEntry entry = entries.get(i); - // If the data is null, that's the server telling us we need to use our default values. - if (entry.getData() == null) { - if (localRegistry == null) { // Lazy initialize - localRegistry = DEFAULTS.get(registryKey); - } - entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId())); - } - - RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, session); - // This is what Geyser wants to keep as a value for this registry. - T cacheEntry = reader.read(context); - if (cacheEntry == null) { - // Registry readers should never return null, rather return a default value - throw new IllegalStateException("Registry reader returned null for an entry!"); - } - builder.add(i, new RegistryEntryData<>(i, entry.getId(), cacheEntry)); - } - registry.reset(builder); - }); - } - - private static void register(JavaRegistryKey registryKey, RegistryLoader reader) { + if (READERS.containsKey(registryKey)) { + throw new IllegalStateException("Tried to register registry reader for " + registryKey + " twice!"); + } READERS.put(registryKey, reader); } @@ -214,10 +221,4 @@ public final class RegistryCache { T read(RegistryEntryContext context); } - - @FunctionalInterface - private interface RegistryLoader { - - void load(GeyserSession session, JavaRegistry registry, List entries); - } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java index e6c6a05de..b6e80bf17 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java @@ -44,7 +44,6 @@ import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.ListRegistry; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.dialog.Dialog; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; @@ -152,19 +151,19 @@ public class JavaRegistries { RegistryIdentifierObjectMapper identifierObjectMapper) implements JavaRegistryKey.RegistryLookup { @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, int networkId) { + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, int networkId) { return Optional.ofNullable(registry.get(networkId)) .map(value -> new RegistryEntryData<>(networkId, Objects.requireNonNull(objectIdentifierMapper.get(value)), value)); } @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, Key key) { + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, Key key) { Optional object = identifierObjectMapper.get(key); return object.map(value -> new RegistryEntryData<>(networkMapper.get(value), key, value)); } @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, T object) { + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, T object) { int id = networkMapper.get(object); return Optional.ofNullable(registry.get(id)) .map(value -> new RegistryEntryData<>(id, Objects.requireNonNull(objectIdentifierMapper.get(value)), value)); @@ -174,22 +173,18 @@ public class JavaRegistries { private static class RegistryCacheLookup implements JavaRegistryKey.RegistryLookup { @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, int networkId) { - return Optional.ofNullable(registry(session, registryKey).entryById(networkId)); + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, int networkId) { + return registries.registry(registryKey).entryById(networkId); } @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, Key key) { - return Optional.ofNullable(registry(session, registryKey).entryByKey(key)); + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, Key key) { + return registries.registry(registryKey).entryByKey(key); } @Override - public Optional> entry(GeyserSession session, JavaRegistryKey registryKey, T object) { - return Optional.ofNullable(registry(session, registryKey).entryByValue(object)); - } - - private JavaRegistry registry(GeyserSession session, JavaRegistryKey key) { - return session.getRegistryCache().registry(key); + public Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registryKey, T object) { + return registries.registry(registryKey).entryByValue(object); } } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java index fc9c7d884..f7c5e6155 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java @@ -30,6 +30,7 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; +import java.util.Optional; /** * A wrapper for a list, holding Java registry values. @@ -39,50 +40,84 @@ public interface JavaRegistry { /** * Looks up a registry entry by its ID. The object can be null, or not present. */ - @Nullable T byId(@NonNegative int id); + default @Nullable T byId(@NonNegative int id) { + return entryById(id).map(RegistryEntryData::data).orElse(null); + } /** * Looks up a registry entry by its ID, and returns it wrapped in {@link RegistryEntryData} so that its registered key is also known. The object can be null, or not present. */ - @Nullable RegistryEntryData entryById(@NonNegative int id); + default Optional> entryById(@NonNegative int id) { + List> entries = entries(); + if (id < 0 || id >= entries.size()) { + return Optional.empty(); + } + return Optional.of(entries.get(id)); + } /** * Looks up a registry entry by its key. The object can be null, or not present. */ - @Nullable T byKey(Key key); + default @Nullable T byKey(Key key) { + return entryByKey(key).map(RegistryEntryData::data).orElse(null); + } /** * Looks up a registry entry by its key, and returns it wrapped in {@link RegistryEntryData}. The object can be null, or not present. */ - @Nullable RegistryEntryData entryByKey(Key key); + default Optional> entryByKey(Key key) { + List> entries = entries(); + for (RegistryEntryData entry : entries) { + if (entry.key().equals(key)) { + return Optional.of(entry); + } + } + return Optional.empty(); + } /** * Reverse looks-up an object to return its network ID, or -1. */ - int byValue(T value); + default int byValue(T value) { + return entryByValue(value).map(RegistryEntryData::id).orElse(-1); + } /** * Reverse looks-up an object to return it wrapped in {@link RegistryEntryData}, or null. */ - @Nullable RegistryEntryData entryByValue(T value); - - /** - * Resets the objects by these IDs. - */ - void reset(List> values); + default Optional> entryByValue(T value) { + List> entries = entries(); + for (RegistryEntryData entry : entries) { + if (entry.data().equals(value)) { + return Optional.of(entry); + } + } + return Optional.empty(); + } /** * All keys of this registry, as a list. */ - List keys(); + default List keys() { + return entries().stream().map(RegistryEntryData::key).toList(); + } /** * All values of this registry, as a list. */ - List values(); + default List values() { + return entries().stream().map(RegistryEntryData::data).toList(); + } /** * The amount of values registered in this registry. */ - int size(); + default int size() { + return entries().size(); + } + + /** + * All entries of this registry, as a list. + */ + List> entries(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java index 47d532603..7ce9b0bba 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java @@ -51,54 +51,96 @@ public record JavaRegistryKey(Key registryKey, RegistryLookup lookup) { * Converts an object to its network ID, or -1 if it is not registered. */ public int networkId(GeyserSession session, T object) { - return entry(session, object).map(RegistryEntryData::id).orElse(-1); + return networkId(session.getRegistryCache(), object); + } + + /** + * Converts an object to its network ID, or -1 if it is not registered. + */ + public int networkId(JavaRegistryProvider registries, T object) { + return entry(registries, object).map(RegistryEntryData::id).orElse(-1); } /** * Converts a registered key to its network ID, or -1 if it is not registered. */ public int networkId(GeyserSession session, Key key) { - return entry(session, key).map(RegistryEntryData::id).orElse(-1); + return networkId(session.getRegistryCache(), key); + } + + /** + * Converts a registered key to its network ID, or -1 if it is not registered. + */ + public int networkId(JavaRegistryProvider registries, Key key) { + return entry(registries, key).map(RegistryEntryData::id).orElse(-1); } /** * Converts an object to its registered key, or null if it is not registered. */ public @Nullable Key key(GeyserSession session, T object) { - return entry(session, object).map(RegistryEntryData::key).orElse(null); + return key(session.getRegistryCache(), object); + } + + /** + * Converts an object to its registered key, or null if it is not registered. + */ + public @Nullable Key key(JavaRegistryProvider registries, T object) { + return entry(registries, object).map(RegistryEntryData::key).orElse(null); } /** * Converts a network ID to its registered key, or null if it is not registered. */ public @Nullable Key key(GeyserSession session, int networkId) { - return entry(session, networkId).map(RegistryEntryData::key).orElse(null); + return key(session.getRegistryCache(), networkId); + } + + /** + * Converts a network ID to its registered key, or null if it is not registered. + */ + public @Nullable Key key(JavaRegistryProvider registries, int networkId) { + return entry(registries, networkId).map(RegistryEntryData::key).orElse(null); } /** * Converts a network ID to an object in this registry, or null if it is not registered. */ public @Nullable T value(GeyserSession session, int networkId) { - return entry(session, networkId).map(RegistryEntryData::data).orElse(null); + return value(session.getRegistryCache(), networkId); + } + + /** + * Converts a network ID to an object in this registry, or null if it is not registered. + */ + public @Nullable T value(JavaRegistryProvider registries, int networkId) { + return entry(registries, networkId).map(RegistryEntryData::data).orElse(null); } /** * Converts a key to an object in this registry, or null if it is not registered. */ public @Nullable T value(GeyserSession session, Key key) { - return entry(session, key).map(RegistryEntryData::data).orElse(null); + return value(session.getRegistryCache(), key); } - private Optional> entry(GeyserSession session, T object) { - return lookup.entry(session, this, object); + /** + * Converts a key to an object in this registry, or null if it is not registered. + */ + public @Nullable T value(JavaRegistryProvider registries, Key key) { + return entry(registries, key).map(RegistryEntryData::data).orElse(null); } - private Optional> entry(GeyserSession session, int networkId) { - return lookup.entry(session, this, networkId); + private Optional> entry(JavaRegistryProvider registries, T object) { + return lookup.entry(registries, this, object); } - private Optional> entry(GeyserSession session, Key key) { - return lookup.entry(session, this, key); + private Optional> entry(JavaRegistryProvider registries, int networkId) { + return lookup.entry(registries, this, networkId); + } + + private Optional> entry(JavaRegistryProvider registries, Key key) { + return lookup.entry(registries, this, key); } /** @@ -106,11 +148,11 @@ public record JavaRegistryKey(Key registryKey, RegistryLookup lookup) { */ public interface RegistryLookup { - Optional> entry(GeyserSession session, JavaRegistryKey registry, int networkId); + Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registry, int networkId); - Optional> entry(GeyserSession session, JavaRegistryKey registry, Key key); + Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registry, Key key); - Optional> entry(GeyserSession session, JavaRegistryKey registry, T object); + Optional> entry(JavaRegistryProvider registries, JavaRegistryKey registry, T object); } @Override diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryProvider.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryProvider.java new file mode 100644 index 000000000..a9cd4592e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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.registry; + +@FunctionalInterface +public interface JavaRegistryProvider { + + JavaRegistry registry(JavaRegistryKey registryKey); +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java index 3c5818c88..ff684f8b8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryContext.java @@ -26,10 +26,12 @@ package org.geysermc.geyser.session.cache.registry; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import java.util.Map; +import java.util.Optional; + import net.kyori.adventure.key.Key; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry; /** @@ -37,9 +39,9 @@ import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry; * * @param entry the registry entry being read. * @param keyIdMap a map for each of the resource location's in the registry and their respective network IDs. - * @param session the Geyser session. + * @param session the Geyser session. Only empty during testing. */ -public record RegistryEntryContext(RegistryEntry entry, Object2IntMap keyIdMap, GeyserSession session) { +public record RegistryEntryContext(RegistryEntry entry, Object2IntMap keyIdMap, Optional session) { // TODO: not a fan of this. With JavaRegistryKey#key now being a thing, I'd rather have that always used, so that registry readers won't have to worry // about using the right method. This would require pre-populating all data-driven registries with default (probably null) values before actually decoding the data from the registy packet. @@ -56,4 +58,8 @@ public record RegistryEntryContext(RegistryEntry entry, Object2IntMap keyId public NbtMap data() { return entry.getData(); } + + public String deserializeDescription() { + return session.map(present -> MessageTranslator.deserializeDescription(present, data())).orElse("MISSING GEYSER SESSION"); + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java index f6c7769ca..8049fdec9 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java @@ -26,95 +26,25 @@ package org.geysermc.geyser.session.cache.registry; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.kyori.adventure.key.Key; -import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; public class SimpleJavaRegistry implements JavaRegistry { - protected final ObjectArrayList> values = new ObjectArrayList<>(); + protected final ObjectArrayList> entries = new ObjectArrayList<>(); - @Override - public T byId(@NonNegative int id) { - if (id < 0 || id >= this.values.size()) { - return null; - } - return this.values.get(id).data(); + public void reset(List> entries) { + this.entries.clear(); + this.entries.addAll(entries); + this.entries.trim(); } @Override - public RegistryEntryData entryById(@NonNegative int id) { - if (id < 0 || id >= this.values.size()) { - return null; - } - return this.values.get(id); - } - - @Override - public T byKey(Key key) { - for (RegistryEntryData entry : values) { - if (entry.key().equals(key)) { - return entry.data(); - } - } - return null; - } - - @Override - public @Nullable RegistryEntryData entryByKey(Key key) { - for (RegistryEntryData entry : values) { - if (entry.key().equals(key)) { - return entry; - } - } - return null; - } - - @Override - public int byValue(T value) { - for (int i = 0; i < this.values.size(); i++) { - if (values.get(i).data().equals(value)) { - return i; - } - } - return -1; - } - - @Override - public RegistryEntryData entryByValue(T value) { - for (RegistryEntryData entry : this.values) { - if (entry.data().equals(value)) { - return entry; - } - } - return null; - } - - @Override - public void reset(List> values) { - this.values.clear(); - this.values.addAll(values); - this.values.trim(); - } - - @Override - public List keys() { - return this.values.stream().map(RegistryEntryData::key).toList(); - } - - @Override - public List values() { - return this.values.stream().map(RegistryEntryData::data).toList(); - } - - @Override - public int size() { - return values.size(); + public List> entries() { + return entries; } @Override public String toString() { - return this.values.toString(); + return entries.toString(); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java index 85c7d762e..42f8e10a5 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/tags/GeyserHolderSet.java @@ -57,6 +57,7 @@ import java.util.function.ToIntFunction; */ @Data public final class GeyserHolderSet { + private static final int[] EMPTY = new int[0]; private final JavaRegistryKey registry; private final @Nullable Tag tag; @@ -86,6 +87,13 @@ public final class GeyserHolderSet { this.inline = inline; } + /** + * Constructs an empty {@link GeyserHolderSet}. + */ + public static GeyserHolderSet empty(JavaRegistryKey registry) { + return new GeyserHolderSet<>(registry, EMPTY); + } + /** * Constructs a {@link GeyserHolderSet} from a MCPL HolderSet. */ diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/ConfirmationDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/ConfirmationDialog.java index c8af7f564..6d5abda81 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/ConfirmationDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/ConfirmationDialog.java @@ -40,7 +40,7 @@ public class ConfirmationDialog extends DialogWithButtons { private final DialogButton yes; private final DialogButton no; - public ConfirmationDialog(GeyserSession session, NbtMap map, IdGetter idGetter) { + public ConfirmationDialog(Optional session, NbtMap map, IdGetter idGetter) { super(session, map, Optional.empty()); yes = DialogButton.read(session, map.get("yes"), idGetter).orElseThrow(); no = DialogButton.read(session, map.get("no"), idGetter).orElseThrow(); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java index e7eb7d82f..7bd420b43 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/Dialog.java @@ -72,7 +72,7 @@ public abstract class Dialog { @Getter private final ParsedInputs defaultInputs; - protected Dialog(GeyserSession session, NbtMap map) { + protected Dialog(Optional session, NbtMap map) { title = MessageTranslator.convertFromNullableNbtTag(session, map.get("title")); externalTitle = Optional.ofNullable(MessageTranslator.convertFromNullableNbtTag(session, map.get("external_title"))); canCloseWithEscape = map.getBoolean("can_close_with_escape", true); @@ -103,10 +103,10 @@ public abstract class Dialog { defaultInputs = inputs.isEmpty() ? ParsedInputs.EMPTY : new ParsedInputs(inputs); } - private static Optional readBody(GeyserSession session, NbtMap tag) { + private static Optional readBody(Optional session, NbtMap tag) { Key type = MinecraftKey.key(tag.getString("type")); if (type.equals(PLAIN_MESSAGE_BODY)) { - return Optional.of(MessageTranslator.convertFromNullableNbtTag(session, tag.get("contents"))); + return Optional.ofNullable(MessageTranslator.convertFromNullableNbtTag(session, tag.get("contents"))); } // Other type is item, can't display that in forms return Optional.empty(); @@ -164,7 +164,7 @@ public abstract class Dialog { return readDialogFromNbt(context.session(), context.data(), context::getNetworkId); } - public static Dialog readDialogFromNbt(GeyserSession session, NbtMap map, IdGetter idGetter) { + public static Dialog readDialogFromNbt(Optional session, NbtMap map, IdGetter idGetter) { Key type = MinecraftKey.key(map.getString("type")); if (type.equals(NoticeDialog.TYPE)) { return new NoticeDialog(session, map, idGetter); @@ -185,7 +185,7 @@ public abstract class Dialog { if (holder.isId()) { return Objects.requireNonNull(JavaRegistries.DIALOG.value(session, holder.id())); } else { - return Dialog.readDialogFromNbt(session, holder.custom(), key -> JavaRegistries.DIALOG.networkId(session, key)); + return Dialog.readDialogFromNbt(Optional.of(session), holder.custom(), key -> JavaRegistries.DIALOG.networkId(session, key)); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogButton.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogButton.java index cab427f29..6bb0cf7e4 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogButton.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogButton.java @@ -36,7 +36,7 @@ import java.util.Optional; public record DialogButton(String label, Optional action) { - public static List readList(GeyserSession session, List tag, Dialog.IdGetter idGetter) { + public static List readList(Optional session, List tag, Dialog.IdGetter idGetter) { if (tag == null) { return List.of(); } @@ -47,7 +47,7 @@ public record DialogButton(String label, Optional action) { return buttons; } - public static Optional read(GeyserSession session, Object tag, Dialog.IdGetter idGetter) { + public static Optional read(Optional session, Object tag, Dialog.IdGetter idGetter) { if (!(tag instanceof NbtMap map)) { return Optional.empty(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogListDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogListDialog.java index a9ff1fc54..29977748e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogListDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogListDialog.java @@ -42,7 +42,7 @@ public class DialogListDialog extends DialogWithButtons { private final GeyserHolderSet dialogs; - public DialogListDialog(GeyserSession session, NbtMap map, IdGetter idGetter) { + public DialogListDialog(Optional session, NbtMap map, IdGetter idGetter) { super(session, map, readDefaultExitAction(session, map, idGetter)); dialogs = GeyserHolderSet.readHolderSet(JavaRegistries.DIALOG, map.get("dialogs"), idGetter, dialog -> Dialog.readDialogFromNbt(session, dialog, idGetter)); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogWithButtons.java b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogWithButtons.java index b4a83dac5..5ecd0acd6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/DialogWithButtons.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/DialogWithButtons.java @@ -40,7 +40,7 @@ public abstract class DialogWithButtons extends Dialog { protected final Optional exitAction; - protected DialogWithButtons(GeyserSession session, NbtMap map, Optional exitAction) { + protected DialogWithButtons(Optional session, NbtMap map, Optional exitAction) { super(session, map); this.exitAction = exitAction; } @@ -91,7 +91,7 @@ public abstract class DialogWithButtons extends Dialog { return exitAction; } - protected static Optional readDefaultExitAction(GeyserSession session, NbtMap map, IdGetter idGetter) { + protected static Optional readDefaultExitAction(Optional session, NbtMap map, IdGetter idGetter) { return DialogButton.read(session, map.get("exit_action"), idGetter); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/MultiActionDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/MultiActionDialog.java index 522a6c7b9..906549bb5 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/MultiActionDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/MultiActionDialog.java @@ -32,6 +32,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.MinecraftKey; import java.util.List; +import java.util.Optional; public class MultiActionDialog extends DialogWithButtons { @@ -39,7 +40,7 @@ public class MultiActionDialog extends DialogWithButtons { private final List buttons; - protected MultiActionDialog(GeyserSession session, NbtMap map, IdGetter idGetter) { + protected MultiActionDialog(Optional session, NbtMap map, IdGetter idGetter) { super(session, map, readDefaultExitAction(session, map, idGetter)); buttons = DialogButton.readList(session, map.getList("actions", NbtType.COMPOUND), idGetter); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java index 589f24ac4..516879a00 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/NoticeDialog.java @@ -41,7 +41,7 @@ public class NoticeDialog extends Dialog { private final Optional button; - public NoticeDialog(GeyserSession session, NbtMap map, Dialog.IdGetter idGetter) { + public NoticeDialog(Optional session, NbtMap map, Dialog.IdGetter idGetter) { super(session, map); button = DialogButton.read(session, map.getCompound("action"), idGetter); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/ServerLinksDialog.java b/core/src/main/java/org/geysermc/geyser/session/dialog/ServerLinksDialog.java index c98d7b6e8..52c1c92c5 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/ServerLinksDialog.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/ServerLinksDialog.java @@ -42,7 +42,7 @@ public class ServerLinksDialog extends DialogWithButtons { public static final Key TYPE = MinecraftKey.key("server_links"); - protected ServerLinksDialog(GeyserSession session, NbtMap map, IdGetter idGetter) { + protected ServerLinksDialog(Optional session, NbtMap map, IdGetter idGetter) { super(session, map, readDefaultExitAction(session, map, idGetter)); } diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/input/BooleanInput.java b/core/src/main/java/org/geysermc/geyser/session/dialog/input/BooleanInput.java index e2388b970..cf836f29b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/input/BooleanInput.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/input/BooleanInput.java @@ -43,7 +43,7 @@ public class BooleanInput extends DialogInput { private final String onTrue; private final String onFalse; - public BooleanInput(GeyserSession session, NbtMap map) { + public BooleanInput(Optional session, NbtMap map) { super(session, map); initial = map.getBoolean("initial", false); onTrue = map.getString("on_true", "true"); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/input/DialogInput.java b/core/src/main/java/org/geysermc/geyser/session/dialog/input/DialogInput.java index 805df0751..c90ef8659 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/input/DialogInput.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/input/DialogInput.java @@ -40,7 +40,7 @@ public abstract class DialogInput { protected final String key; protected final String label; - protected DialogInput(GeyserSession session, NbtMap map) { + protected DialogInput(Optional session, NbtMap map) { this.key = map.getString("key"); this.label = MessageTranslator.convertFromNullableNbtTag(session, map.get("label")); } @@ -59,7 +59,7 @@ public abstract class DialogInput { public abstract T defaultValue(); - public static DialogInput read(GeyserSession session, NbtMap tag) { + public static DialogInput read(Optional session, NbtMap tag) { Key type = MinecraftKey.key(tag.getString("type")); if (type.equals(BooleanInput.TYPE)) { return new BooleanInput(session, tag); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/input/NumberRangeInput.java b/core/src/main/java/org/geysermc/geyser/session/dialog/input/NumberRangeInput.java index a6a2fd41e..8e7872ba2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/input/NumberRangeInput.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/input/NumberRangeInput.java @@ -44,7 +44,7 @@ public class NumberRangeInput extends DialogInput { private final float initial; private final float step; - public NumberRangeInput(GeyserSession session, NbtMap map) { + public NumberRangeInput(Optional session, NbtMap map) { super(session, map); start = map.getFloat("start"); end = map.getFloat("end"); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/input/SingleOptionInput.java b/core/src/main/java/org/geysermc/geyser/session/dialog/input/SingleOptionInput.java index f996f3da0..abf806436 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/input/SingleOptionInput.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/input/SingleOptionInput.java @@ -47,7 +47,7 @@ public class SingleOptionInput extends DialogInput { private final List entries = new ArrayList<>(); private final int initial; - public SingleOptionInput(GeyserSession session, NbtMap map) { + public SingleOptionInput(Optional session, NbtMap map) { super(session, map); labelVisible = map.getBoolean("label_visible", true); List entriesTag = map.getList("options", NbtType.COMPOUND); diff --git a/core/src/main/java/org/geysermc/geyser/session/dialog/input/TextInput.java b/core/src/main/java/org/geysermc/geyser/session/dialog/input/TextInput.java index 35ba4cfe4..a8ef5466e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/dialog/input/TextInput.java +++ b/core/src/main/java/org/geysermc/geyser/session/dialog/input/TextInput.java @@ -43,7 +43,7 @@ public class TextInput extends DialogInput { private final String initial; private final int maxLength; - public TextInput(GeyserSession session, NbtMap map) { + public TextInput(Optional session, NbtMap map) { super(session, map); labelVisible = map.getBoolean("label_visible", true); initial = map.getString("initial", ""); diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 3361ad539..e6680a723 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.text; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -513,11 +514,15 @@ public class MessageTranslator { return convertMessageForTooltip(parsed, session.locale()); } - public static @Nullable String convertFromNullableNbtTag(GeyserSession session, @Nullable Object nbtTag) { + /** + * Should only be used by {@link org.geysermc.geyser.session.cache.RegistryCache.RegistryReader}s, as these do not always have a {@link GeyserSession} available. + */ + public static @Nullable String convertFromNullableNbtTag(Optional session, @Nullable Object nbtTag) { if (nbtTag == null) { return null; } - return convertMessage(session, componentFromNbtTag(nbtTag)); + return session.map(present -> convertMessage(present, componentFromNbtTag(nbtTag))) + .orElse("MISSING GEYSER SESSION"); } public static Component componentFromNbtTag(Object nbtTag) {