1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Refactor Java registry code to be more abstract, and other minor clean-ups (#5926)

* Create abstract JavaRegistryProvider interface, remove GeyserSession from MinecraftHashEncoder, add default implementations to JavaRegistry

* Make GeyserSession in RegistryEntryContext optional

* Expose READERS map as visible for testing in RegistryCache

* Add inlineNbtMap helper to MapBuilder

* Mark JavaRegistryProvider as functional interface
This commit is contained in:
Eclipse
2025-10-23 22:45:30 +00:00
committed by GitHub
parent 7739b0d91c
commit e2a82b3659
32 changed files with 337 additions and 269 deletions

View File

@@ -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();

View File

@@ -68,16 +68,18 @@ public final class TrimRecipe {
int networkId = context.getNetworkId(context.id());
ItemMapping trimItem = null;
for (ProvidesTrimMaterial provider : materialProviders().keySet()) {
Holder<ArmorTrim.TrimMaterial> 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<ArmorTrim.TrimMaterial> 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);
}

View File

@@ -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<EnchantmentComponent> effects,
NbtMap data = context.data();
Set<EnchantmentComponent> effects = readEnchantmentComponents(data.getCompound("effects"));
GeyserHolderSet<Item> supportedItems = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ITEM, data.get("supported_items"));
GeyserHolderSet<Item> 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<EnchantmentComponent> 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);
}

View File

@@ -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 <T> HashCode hash(GeyserSession session, DataComponentType<T> 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!");

View File

@@ -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<Type> extends UnaryOperator<MapHasher<Type>> {
static <Type> MapBuilder<Type> 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.
*
* <p>Can be used with {@link MapHasher#accept(Function, Function)} to inline an NBT map into a map builder, together with other keys.</p>
*
* @param <Type> the type to encode.
*/
static <Type> Function<NbtMap, MapBuilder<Type>> 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;
};
}
}

View File

@@ -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<Type> {
return this;
}
private MapHasher<Type> 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<Type> {
return accept(key, hasher.hash(value, encoder));
}
public MapHasher<Type> inlineNbt(Function<Type, NbtMap> 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.
*

View File

@@ -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() {

View File

@@ -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;
*
* <ul>
* <li>{@link MinecraftHasher#list()} creates a hasher that hashes a list of the objects.</li>
* <li>{@link MinecraftHasher#cast(Function)} and {@link MinecraftHasher#sessionCast(BiFunction)} create a new hasher that delegates to this hasher with a converter function.</li>
* <li>{@link MinecraftHasher#cast(Function)} and {@link MinecraftHasher#registryCast(BiFunction)} create a new hasher that delegates to this hasher with a converter function.</li>
* <li>{@link MinecraftHasher#filterable()} creates a hasher that hashes a {@link Filterable} instance of the object.</li>
* </ul>
*
@@ -108,13 +107,17 @@ public interface MinecraftHasher<Type> {
MinecraftHasher<Boolean> BOOL = (b, encoder) -> encoder.bool(b);
MinecraftHasher<IntStream> INT_ARRAY = (ints, encoder) -> encoder.intArray(ints.toArray());
MinecraftHasher<byte[]> BYTE_ARRAY = (ints, encoder) -> encoder.byteArray(ints);
MinecraftHasher<int[]> INT_ARRAY = (ints, encoder) -> encoder.intArray(ints);
MinecraftHasher<long[]> LONG_ARRAY = (ints, encoder) -> encoder.longArray(ints);
MinecraftHasher<NbtMap> NBT_MAP = (map, encoder) -> encoder.nbtMap(map);
MinecraftHasher<NbtList<?>> NBT_LIST = (list, encoder) -> encoder.nbtList(list);
MinecraftHasher<Vector3i> POS = INT_ARRAY.cast(pos -> IntStream.of(pos.getX(), pos.getY(), pos.getZ()));
MinecraftHasher<Vector3i> POS = INT_ARRAY.cast(pos -> new int[]{pos.getX(), pos.getY(), pos.getZ()});
MinecraftHasher<Key> KEY = STRING.cast(Key::asString);
@@ -125,7 +128,7 @@ public interface MinecraftHasher<Type> {
MinecraftHasher<UUID> 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<GameProfile.Property> GAME_PROFILE_PROPERTY = mapBuilder(builder -> builder
@@ -188,25 +191,25 @@ public interface MinecraftHasher<Type> {
/**
* "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.
*
* <p>If a {@link GeyserSession} object is needed for conversion, use {@link MinecraftHasher#sessionCast(BiFunction)}.</p>
* <p>If a {@link JavaRegistryProvider} object is needed for conversion, use {@link MinecraftHasher#registryCast(BiFunction)}.</p>
*
* @param converter the converter function that converts a {@link Casted} into a {@link Type}.
* @param <Casted> the type of the new hasher.
* @see MinecraftHasher#sessionCast(BiFunction)
* @see MinecraftHasher#registryCast(BiFunction)
*/
default <Casted> MinecraftHasher<Casted> cast(Function<Casted, Type> 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 <Casted> the type of the new hasher.
* @see MinecraftHasher#cast(Function)
*/
default <Casted> MinecraftHasher<Casted> sessionCast(BiFunction<GeyserSession, Casted, Type> converter) {
return (value, encoder) -> hash(converter.apply(encoder.session(), value), encoder);
default <Casted> MinecraftHasher<Casted> registryCast(BiFunction<JavaRegistryProvider, Casted, Type> converter) {
return (value, encoder) -> hash(converter.apply(encoder.registries(), value), encoder);
}
/**

View File

@@ -351,7 +351,7 @@ public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
MinecraftHasher<BeehiveOccupant> 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<DirectType> extends MinecraftHasher<Integer> {
* @param registry the registry to create a hasher for.
*/
static RegistryHasher<?> registry(JavaRegistryKey<?> registry) {
MinecraftHasher<Integer> hasher = KEY.sessionCast(registry::key);
MinecraftHasher<Integer> hasher = KEY.registryCast(registry::key);
return hasher::hash;
}

View File

@@ -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());
}
}

View File

@@ -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.
* <p>Crafted as of 1.20.5 for easy "add new registry" functionality in the future.</p>
*/
public final class RegistryCache {
public final class RegistryCache implements JavaRegistryProvider {
private static final Map<JavaRegistryKey<?>, Map<Key, NbtMap>> DEFAULTS;
private static final Map<JavaRegistryKey<?>, RegistryLoader<?>> READERS = new HashMap<>();
@VisibleForTesting
public static final Map<JavaRegistryKey<?>, 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<JavaRegistryKey<?>, JavaRegistry<?>> registries;
private final Reference2ObjectMap<JavaRegistryKey<?>, 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 <T> JavaRegistry<T> registry(JavaRegistryKey<T> 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<T>) registries.get(registryKey);
}
private static <T> void readRegistry(GeyserSession session, JavaRegistryKey<T> registryKey, SimpleJavaRegistry<T> registry,
RegistryReader<T> reader, List<RegistryEntry> entries) {
Map<Key, NbtMap> 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<Key> entryIdMap = new Object2IntOpenHashMap<>();
for (int i = 0; i < entries.size(); i++) {
entryIdMap.put(entries.get(i).getId(), i);
}
List<RegistryEntryData<T>> 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 <T> the class that represents these entries.
*/
private static <T> void register(JavaRegistryKey<T> registryKey, RegistryReader<T> reader) {
register(registryKey, (session, registry, entries) -> {
Map<Key, NbtMap> 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<Key> entryIdMap = new Object2IntOpenHashMap<>();
for (int i = 0; i < entries.size(); i++) {
entryIdMap.put(entries.get(i).getId(), i);
}
List<RegistryEntryData<T>> 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 <T> void register(JavaRegistryKey<T> registryKey, RegistryLoader<T> 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<T> {
void load(GeyserSession session, JavaRegistry<T> registry, List<RegistryEntry> entries);
}
}

View File

@@ -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<T> identifierObjectMapper) implements JavaRegistryKey.RegistryLookup<T> {
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, int networkId) {
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registryKey, int networkId) {
return Optional.ofNullable(registry.get(networkId))
.map(value -> new RegistryEntryData<>(networkId, Objects.requireNonNull(objectIdentifierMapper.get(value)), value));
}
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, Key key) {
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registryKey, Key key) {
Optional<T> object = identifierObjectMapper.get(key);
return object.map(value -> new RegistryEntryData<>(networkMapper.get(value), key, value));
}
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, T object) {
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> 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<T> implements JavaRegistryKey.RegistryLookup<T> {
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, int networkId) {
return Optional.ofNullable(registry(session, registryKey).entryById(networkId));
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registryKey, int networkId) {
return registries.registry(registryKey).entryById(networkId);
}
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, Key key) {
return Optional.ofNullable(registry(session, registryKey).entryByKey(key));
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registryKey, Key key) {
return registries.registry(registryKey).entryByKey(key);
}
@Override
public Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registryKey, T object) {
return Optional.ofNullable(registry(session, registryKey).entryByValue(object));
}
private JavaRegistry<T> registry(GeyserSession session, JavaRegistryKey<T> key) {
return session.getRegistryCache().registry(key);
public Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registryKey, T object) {
return registries.registry(registryKey).entryByValue(object);
}
}
}

View File

@@ -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<T> {
/**
* 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<T> entryById(@NonNegative int id);
default Optional<RegistryEntryData<T>> entryById(@NonNegative int id) {
List<RegistryEntryData<T>> 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<T> entryByKey(Key key);
default Optional<RegistryEntryData<T>> entryByKey(Key key) {
List<RegistryEntryData<T>> entries = entries();
for (RegistryEntryData<T> 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<T> entryByValue(T value);
/**
* Resets the objects by these IDs.
*/
void reset(List<RegistryEntryData<T>> values);
default Optional<RegistryEntryData<T>> entryByValue(T value) {
List<RegistryEntryData<T>> entries = entries();
for (RegistryEntryData<T> entry : entries) {
if (entry.data().equals(value)) {
return Optional.of(entry);
}
}
return Optional.empty();
}
/**
* All keys of this registry, as a list.
*/
List<Key> keys();
default List<Key> keys() {
return entries().stream().map(RegistryEntryData::key).toList();
}
/**
* All values of this registry, as a list.
*/
List<T> values();
default List<T> 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<RegistryEntryData<T>> entries();
}

View File

@@ -51,54 +51,96 @@ public record JavaRegistryKey<T>(Key registryKey, RegistryLookup<T> 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<RegistryEntryData<T>> 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<RegistryEntryData<T>> entry(GeyserSession session, int networkId) {
return lookup.entry(session, this, networkId);
private Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, T object) {
return lookup.entry(registries, this, object);
}
private Optional<RegistryEntryData<T>> entry(GeyserSession session, Key key) {
return lookup.entry(session, this, key);
private Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, int networkId) {
return lookup.entry(registries, this, networkId);
}
private Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, Key key) {
return lookup.entry(registries, this, key);
}
/**
@@ -106,11 +148,11 @@ public record JavaRegistryKey<T>(Key registryKey, RegistryLookup<T> lookup) {
*/
public interface RegistryLookup<T> {
Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registry, int networkId);
Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registry, int networkId);
Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registry, Key key);
Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registry, Key key);
Optional<RegistryEntryData<T>> entry(GeyserSession session, JavaRegistryKey<T> registry, T object);
Optional<RegistryEntryData<T>> entry(JavaRegistryProvider registries, JavaRegistryKey<T> registry, T object);
}
@Override

View File

@@ -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 {
<T> JavaRegistry<T> registry(JavaRegistryKey<T> registryKey);
}

View File

@@ -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<Key> keyIdMap, GeyserSession session) {
public record RegistryEntryContext(RegistryEntry entry, Object2IntMap<Key> keyIdMap, Optional<GeyserSession> 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<Key> keyId
public NbtMap data() {
return entry.getData();
}
public String deserializeDescription() {
return session.map(present -> MessageTranslator.deserializeDescription(present, data())).orElse("MISSING GEYSER SESSION");
}
}

View File

@@ -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<T> implements JavaRegistry<T> {
protected final ObjectArrayList<RegistryEntryData<T>> values = new ObjectArrayList<>();
protected final ObjectArrayList<RegistryEntryData<T>> 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<RegistryEntryData<T>> entries) {
this.entries.clear();
this.entries.addAll(entries);
this.entries.trim();
}
@Override
public RegistryEntryData<T> 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<T> entry : values) {
if (entry.key().equals(key)) {
return entry.data();
}
}
return null;
}
@Override
public @Nullable RegistryEntryData<T> entryByKey(Key key) {
for (RegistryEntryData<T> 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<T> entryByValue(T value) {
for (RegistryEntryData<T> entry : this.values) {
if (entry.data().equals(value)) {
return entry;
}
}
return null;
}
@Override
public void reset(List<RegistryEntryData<T>> values) {
this.values.clear();
this.values.addAll(values);
this.values.trim();
}
@Override
public List<Key> keys() {
return this.values.stream().map(RegistryEntryData::key).toList();
}
@Override
public List<T> values() {
return this.values.stream().map(RegistryEntryData::data).toList();
}
@Override
public int size() {
return values.size();
public List<RegistryEntryData<T>> entries() {
return entries;
}
@Override
public String toString() {
return this.values.toString();
return entries.toString();
}
}

View File

@@ -57,6 +57,7 @@ import java.util.function.ToIntFunction;
*/
@Data
public final class GeyserHolderSet<T> {
private static final int[] EMPTY = new int[0];
private final JavaRegistryKey<T> registry;
private final @Nullable Tag<T> tag;
@@ -86,6 +87,13 @@ public final class GeyserHolderSet<T> {
this.inline = inline;
}
/**
* Constructs an empty {@link GeyserHolderSet}.
*/
public static <T> GeyserHolderSet<T> empty(JavaRegistryKey<T> registry) {
return new GeyserHolderSet<>(registry, EMPTY);
}
/**
* Constructs a {@link GeyserHolderSet} from a MCPL HolderSet.
*/

View File

@@ -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<GeyserSession> 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();

View File

@@ -72,7 +72,7 @@ public abstract class Dialog {
@Getter
private final ParsedInputs defaultInputs;
protected Dialog(GeyserSession session, NbtMap map) {
protected Dialog(Optional<GeyserSession> 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<String> readBody(GeyserSession session, NbtMap tag) {
private static Optional<String> readBody(Optional<GeyserSession> 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<GeyserSession> 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));
}
}

View File

@@ -36,7 +36,7 @@ import java.util.Optional;
public record DialogButton(String label, Optional<DialogAction> action) {
public static List<DialogButton> readList(GeyserSession session, List<NbtMap> tag, Dialog.IdGetter idGetter) {
public static List<DialogButton> readList(Optional<GeyserSession> session, List<NbtMap> tag, Dialog.IdGetter idGetter) {
if (tag == null) {
return List.of();
}
@@ -47,7 +47,7 @@ public record DialogButton(String label, Optional<DialogAction> action) {
return buttons;
}
public static Optional<DialogButton> read(GeyserSession session, Object tag, Dialog.IdGetter idGetter) {
public static Optional<DialogButton> read(Optional<GeyserSession> session, Object tag, Dialog.IdGetter idGetter) {
if (!(tag instanceof NbtMap map)) {
return Optional.empty();
}

View File

@@ -42,7 +42,7 @@ public class DialogListDialog extends DialogWithButtons {
private final GeyserHolderSet<Dialog> dialogs;
public DialogListDialog(GeyserSession session, NbtMap map, IdGetter idGetter) {
public DialogListDialog(Optional<GeyserSession> 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));
}

View File

@@ -40,7 +40,7 @@ public abstract class DialogWithButtons extends Dialog {
protected final Optional<DialogButton> exitAction;
protected DialogWithButtons(GeyserSession session, NbtMap map, Optional<DialogButton> exitAction) {
protected DialogWithButtons(Optional<GeyserSession> session, NbtMap map, Optional<DialogButton> exitAction) {
super(session, map);
this.exitAction = exitAction;
}
@@ -91,7 +91,7 @@ public abstract class DialogWithButtons extends Dialog {
return exitAction;
}
protected static Optional<DialogButton> readDefaultExitAction(GeyserSession session, NbtMap map, IdGetter idGetter) {
protected static Optional<DialogButton> readDefaultExitAction(Optional<GeyserSession> session, NbtMap map, IdGetter idGetter) {
return DialogButton.read(session, map.get("exit_action"), idGetter);
}
}

View File

@@ -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<DialogButton> buttons;
protected MultiActionDialog(GeyserSession session, NbtMap map, IdGetter idGetter) {
protected MultiActionDialog(Optional<GeyserSession> session, NbtMap map, IdGetter idGetter) {
super(session, map, readDefaultExitAction(session, map, idGetter));
buttons = DialogButton.readList(session, map.getList("actions", NbtType.COMPOUND), idGetter);
}

View File

@@ -41,7 +41,7 @@ public class NoticeDialog extends Dialog {
private final Optional<DialogButton> button;
public NoticeDialog(GeyserSession session, NbtMap map, Dialog.IdGetter idGetter) {
public NoticeDialog(Optional<GeyserSession> session, NbtMap map, Dialog.IdGetter idGetter) {
super(session, map);
button = DialogButton.read(session, map.getCompound("action"), idGetter);
}

View File

@@ -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<GeyserSession> session, NbtMap map, IdGetter idGetter) {
super(session, map, readDefaultExitAction(session, map, idGetter));
}

View File

@@ -43,7 +43,7 @@ public class BooleanInput extends DialogInput<Boolean> {
private final String onTrue;
private final String onFalse;
public BooleanInput(GeyserSession session, NbtMap map) {
public BooleanInput(Optional<GeyserSession> session, NbtMap map) {
super(session, map);
initial = map.getBoolean("initial", false);
onTrue = map.getString("on_true", "true");

View File

@@ -40,7 +40,7 @@ public abstract class DialogInput<T> {
protected final String key;
protected final String label;
protected DialogInput(GeyserSession session, NbtMap map) {
protected DialogInput(Optional<GeyserSession> session, NbtMap map) {
this.key = map.getString("key");
this.label = MessageTranslator.convertFromNullableNbtTag(session, map.get("label"));
}
@@ -59,7 +59,7 @@ public abstract class DialogInput<T> {
public abstract T defaultValue();
public static DialogInput<?> read(GeyserSession session, NbtMap tag) {
public static DialogInput<?> read(Optional<GeyserSession> session, NbtMap tag) {
Key type = MinecraftKey.key(tag.getString("type"));
if (type.equals(BooleanInput.TYPE)) {
return new BooleanInput(session, tag);

View File

@@ -44,7 +44,7 @@ public class NumberRangeInput extends DialogInput<Float> {
private final float initial;
private final float step;
public NumberRangeInput(GeyserSession session, NbtMap map) {
public NumberRangeInput(Optional<GeyserSession> session, NbtMap map) {
super(session, map);
start = map.getFloat("start");
end = map.getFloat("end");

View File

@@ -47,7 +47,7 @@ public class SingleOptionInput extends DialogInput<String> {
private final List<Entry> entries = new ArrayList<>();
private final int initial;
public SingleOptionInput(GeyserSession session, NbtMap map) {
public SingleOptionInput(Optional<GeyserSession> session, NbtMap map) {
super(session, map);
labelVisible = map.getBoolean("label_visible", true);
List<NbtMap> entriesTag = map.getList("options", NbtType.COMPOUND);

View File

@@ -43,7 +43,7 @@ public class TextInput extends DialogInput<String> {
private final String initial;
private final int maxLength;
public TextInput(GeyserSession session, NbtMap map) {
public TextInput(Optional<GeyserSession> session, NbtMap map) {
super(session, map);
labelVisible = map.getBoolean("label_visible", true);
initial = map.getString("initial", "");

View File

@@ -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<GeyserSession> 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) {