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:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
32
core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryProvider.java
vendored
Normal file
32
core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryProvider.java
vendored
Normal 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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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", "");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user