diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java index 660cbd12b..fcc01515a 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/ComponentHasher.java @@ -50,9 +50,9 @@ public interface ComponentHasher { } }); - MinecraftHasher NAMED_COLOR = MinecraftHasher.STRING.convert(NamedTextColor::toString); + MinecraftHasher NAMED_COLOR = MinecraftHasher.STRING.cast(NamedTextColor::toString); - MinecraftHasher DIRECT_COLOR = MinecraftHasher.STRING.convert(TextColor::asHexString); + MinecraftHasher DIRECT_COLOR = MinecraftHasher.STRING.cast(TextColor::asHexString); MinecraftHasher COLOR = (value, encoder) -> { if (value instanceof NamedTextColor named) { @@ -61,13 +61,13 @@ public interface ComponentHasher { return DIRECT_COLOR.hash(value, encoder); }; - MinecraftHasher DECORATION_STATE = MinecraftHasher.BOOL.convert(state -> switch (state) { + MinecraftHasher DECORATION_STATE = MinecraftHasher.BOOL.cast(state -> switch (state) { case NOT_SET -> null; // Should never happen since we're using .optional() with NOT_SET as default value below case FALSE -> false; case TRUE -> true; }); - MinecraftHasher CLICK_EVENT_ACTION = MinecraftHasher.STRING.convert(ClickEvent.Action::toString); + MinecraftHasher CLICK_EVENT_ACTION = MinecraftHasher.STRING.cast(ClickEvent.Action::toString); MinecraftHasher CLICK_EVENT = CLICK_EVENT_ACTION.dispatch("action", ClickEvent::action, action -> switch (action) { case OPEN_URL -> builder -> builder.accept("url", MinecraftHasher.STRING, ClickEvent::value); @@ -77,7 +77,7 @@ public interface ComponentHasher { case COPY_TO_CLIPBOARD -> builder -> builder.accept("value", MinecraftHasher.STRING, ClickEvent::value); }); - MinecraftHasher> HOVER_EVENT_ACTION = MinecraftHasher.STRING.convert(HoverEvent.Action::toString); + MinecraftHasher> HOVER_EVENT_ACTION = MinecraftHasher.STRING.cast(HoverEvent.Action::toString); MinecraftHasher> HOVER_EVENT = HOVER_EVENT_ACTION.dispatch("action", HoverEvent::action, action -> { if (action == HoverEvent.Action.SHOW_TEXT) { @@ -106,7 +106,7 @@ public interface ComponentHasher { .optionalNullable("insertion", MinecraftHasher.STRING, Style::insertion) .optionalNullable("font", MinecraftHasher.KEY, Style::font); - MinecraftHasher SIMPLE_TEXT_COMPONENT = MinecraftHasher.STRING.convert(TextComponent::content); + MinecraftHasher SIMPLE_TEXT_COMPONENT = MinecraftHasher.STRING.cast(TextComponent::content); MinecraftHasher FULL_TEXT_COMPONENT = component(builder -> builder .accept("text", MinecraftHasher.STRING, TextComponent::content)); diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java index cfbc7912b..d055235a3 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java @@ -99,7 +99,7 @@ public class DataComponentHashers { register(DataComponentTypes.CAN_PLACE_ON, RegistryHasher.ADVENTURE_MODE_PREDICATE); register(DataComponentTypes.CAN_BREAK, RegistryHasher.ADVENTURE_MODE_PREDICATE); // TODO needs tests - register(DataComponentTypes.ATTRIBUTE_MODIFIERS, RegistryHasher.ATTRIBUTE_MODIFIER_ENTRY.list().convert(ItemAttributeModifiers::getModifiers)); // TODO needs tests + register(DataComponentTypes.ATTRIBUTE_MODIFIERS, RegistryHasher.ATTRIBUTE_MODIFIER_ENTRY.list().cast(ItemAttributeModifiers::getModifiers)); // TODO needs tests registerMap(DataComponentTypes.CUSTOM_MODEL_DATA, builder -> builder .optionalList("floats", MinecraftHasher.FLOAT, CustomModelData::floats) @@ -122,7 +122,7 @@ public class DataComponentHashers { .optional("can_always_eat", MinecraftHasher.BOOL, FoodProperties::isCanAlwaysEat, false)); registerMap(DataComponentTypes.CONSUMABLE, builder -> builder .optional("consume_seconds", MinecraftHasher.FLOAT, Consumable::consumeSeconds, 1.6F) - .optional("animation", MinecraftHasher.ITEM_USE_ANIMATION, Consumable::animation, Consumable.ItemUseAnimation.EAT) + .optional("animation", RegistryHasher.ITEM_USE_ANIMATION, Consumable::animation, Consumable.ItemUseAnimation.EAT) .optional("sound", RegistryHasher.SOUND_EVENT, Consumable::sound, BuiltinSound.ENTITY_GENERIC_EAT) .optional("has_consume_particles", MinecraftHasher.BOOL, Consumable::hasConsumeParticles, true) .optionalList("on_consume_effects", RegistryHasher.CONSUME_EFFECT, Consumable::onConsumeEffects)); @@ -228,7 +228,7 @@ public class DataComponentHashers { register(DataComponentTypes.BASE_COLOR, MinecraftHasher.DYE_COLOR); register(DataComponentTypes.POT_DECORATIONS, RegistryHasher.ITEM.list()); register(DataComponentTypes.CONTAINER, RegistryHasher.ITEM_CONTAINER_CONTENTS); - register(DataComponentTypes.BLOCK_STATE, MinecraftHasher.map(MinecraftHasher.STRING, MinecraftHasher.STRING).convert(BlockStateProperties::getProperties)); + register(DataComponentTypes.BLOCK_STATE, MinecraftHasher.map(MinecraftHasher.STRING, MinecraftHasher.STRING).cast(BlockStateProperties::getProperties)); register(DataComponentTypes.BEES, RegistryHasher.BEEHIVE_OCCUPANT.list()); register(DataComponentTypes.LOCK, MinecraftHasher.NBT_MAP); @@ -250,7 +250,7 @@ public class DataComponentHashers { register(DataComponentTypes.PIG_VARIANT, RegistryHasher.PIG_VARIANT); register(DataComponentTypes.COW_VARIANT, RegistryHasher.COW_VARIANT); register(DataComponentTypes.CHICKEN_VARIANT, MinecraftHasher.KEY - .sessionConvert((session, holder) -> holder.getOrCompute(id -> JavaRegistries.CHICKEN_VARIANT.keyFromNetworkId(session, id)))); // Why, Mojang? + .sessionCast((session, holder) -> holder.getOrCompute(id -> JavaRegistries.CHICKEN_VARIANT.keyFromNetworkId(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); @@ -284,7 +284,7 @@ public class DataComponentHashers { public static MinecraftHasher hasherOrEmpty(DataComponentType component) { MinecraftHasher hasher = (MinecraftHasher) hashers.get(component); if (hasher == null) { - return MinecraftHasher.UNIT.convert(value -> Unit.INSTANCE); + return MinecraftHasher.UNIT.cast(value -> Unit.INSTANCE); } return hasher; } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java index ab8ca435a..46f523e09 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHashEncoder.java @@ -40,7 +40,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -// Based off the HashOps in mojmap, hashes a component, TODO: documentation +/** + * Encodes primitive Java objects, lists, and maps into a {@link HashCode}, using {@link Hashing#crc32c()} as hash function. + * + *

Based off the {@code HashOps} class in vanilla Java 1.21.5, and is used by {@link MinecraftHasher}.

+ */ @SuppressWarnings("UnstableApiUsage") public class MinecraftHashEncoder { private static final byte TAG_EMPTY = 1; diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java index eaa107ade..931b9b6a0 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/MinecraftHasher.java @@ -33,17 +33,16 @@ 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.item.hashing.data.FireworkExplosionShape; import org.geysermc.geyser.session.GeyserSession; 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; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Filterable; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.UUID; import java.util.function.BiFunction; @@ -53,9 +52,41 @@ import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.IntStream; +/** + * Encodes an object into a {@link HashCode} using a {@link MinecraftHashEncoder}. + * + *

Hashers have been implemented for common types, such as all Java primitives, units (encodes an empty map), NBT maps and lists, + * {@link Vector3i} positions, {@link Key} resource locations and {@code #}-prefixed tags, UUIDs, and more types. Furthermore, in {@link RegistryHasher} + * more hashers can be found for more specific for Minecraft types, and {@link ComponentHasher#COMPONENT} hashes {@link net.kyori.adventure.text.Component}s.

+ * + *

Most hashers are not created by implementing them directly, rather, they are built on top of other hashers, using various methods to manipulate and map them. + * Here are some commonly used ones:

+ * + *
    + *
  • {@link MinecraftHasher#list()} creates a hasher that hashes a list of the objects.
  • + *
  • {@link MinecraftHasher#cast(Function)} and {@link MinecraftHasher#sessionCast(BiFunction)} create a new hasher that delegates to this hasher with a converter function.
  • + *
  • {@link MinecraftHasher#filterable()} creates a hasher that hashes a {@link Filterable} instance of the object.
  • + *
+ * + *

On top of these there are more methods, as well as static helper methods in this class, to create hashers. + * One worth pointing out is {@link MinecraftHasher#mapBuilder(MapBuilder)}, which hashes an object into a map-like structure. Read the documentation there and on {@link MapBuilder} on how to use it.

+ * + *

As of right now, hashers are used to create hash codes for data components, which Minecraft requires to be sent in inventory transactions. You'll find all the hashers for data components in + * {@link DataComponentHashers}. In the future, hashers may be used elsewhere as well. If necessary, this system can even be refactored to write to different data structures, such as NBT or JSON files, as well.

+ * + *

When creating new hashers, please be sure to put them in the proper place:

+ * + *
    + *
  • Hashers that hash very generic types, or types that are used broadly across Minecraft (like key, UUID, game profile, etc.) belong here in {@link MinecraftHasher}.
  • + *
  • Hashers that hash more specific types, are more complicated, or depend on a hasher in {@link RegistryHasher}, belong in {@link RegistryHasher}.
  • + *
  • Hashers that hash components, and are used nowhere else, belong in {@link DataComponentHashers}.
  • + *
+ * + * @param the type this hasher hashes. + */ @SuppressWarnings("UnstableApiUsage") @FunctionalInterface -public interface MinecraftHasher { +public interface MinecraftHasher { MinecraftHasher UNIT = (unit, encoder) -> encoder.emptyMap(); @@ -81,13 +112,13 @@ public interface MinecraftHasher { MinecraftHasher> NBT_LIST = (list, encoder) -> encoder.nbtList(list); - MinecraftHasher POS = INT_ARRAY.convert(pos -> IntStream.of(pos.getX(), pos.getY(), pos.getZ())); + MinecraftHasher POS = INT_ARRAY.cast(pos -> IntStream.of(pos.getX(), pos.getY(), pos.getZ())); - MinecraftHasher KEY = STRING.convert(Key::asString); + MinecraftHasher KEY = STRING.cast(Key::asString); - MinecraftHasher TAG = STRING.convert(key -> "#" + key.asString()); + MinecraftHasher TAG = STRING.cast(key -> "#" + key.asString()); - MinecraftHasher UUID = INT_ARRAY.convert(uuid -> { + MinecraftHasher UUID = INT_ARRAY.cast(uuid -> { long mostSignificant = uuid.getMostSignificantBits(); long leastSignificant = uuid.getLeastSignificantBits(); return IntStream.of((int) (mostSignificant >> 32), (int) mostSignificant, (int) (leastSignificant >> 32), (int) leastSignificant); @@ -107,8 +138,6 @@ public interface MinecraftHasher { MinecraftHasher DYE_COLOR = fromIdEnum(DyeColor.values(), DyeColor::getJavaIdentifier); - MinecraftHasher ITEM_USE_ANIMATION = fromEnum(); - MinecraftHasher EQUIPMENT_SLOT = fromEnum(slot -> switch (slot) { case MAIN_HAND -> "mainhand"; case OFF_HAND -> "offhand"; @@ -138,77 +167,183 @@ public interface MinecraftHasher { .accept("dimension", KEY, GlobalPos::getDimension) .accept("pos", POS, GlobalPos::getPosition)); - MinecraftHasher FIREWORK_EXPLOSION_SHAPE = fromIdEnum(FireworkExplosionShape.values()); + HashCode hash(Type value, MinecraftHashEncoder encoder); - HashCode hash(T value, MinecraftHashEncoder encoder); - - default MinecraftHasher> list() { + /** + * Creates a hasher that hashes a list of objects this hasher hashes. + */ + default MinecraftHasher> list() { return (list, encoder) -> encoder.list(list.stream().map(element -> hash(element, encoder)).toList()); } - default MinecraftHasher> filterable() { + /** + * "Casts" this hasher to another hash a different object, with a converter method. The returned hasher takes a {@link Casted}, converts it to a {@link Type} using the {@code converter}, and then hashes it. + * + *

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

+ * + * @param converter the converter function that converts a {@link Casted} into a {@link Type}. + * @param the type of the new hasher. + * @see MinecraftHasher#sessionCast(BiFunction) + */ + default MinecraftHasher cast(Function converter) { + return (value, encoder) -> hash(converter.apply(value), encoder); + } + + /** + * Like {@link MinecraftHasher#cast(Function)}, but has access to {@link GeyserSession}. + * + * @param converter the converter function. + * @param the type of the new hasher. + * @see MinecraftHasher#cast(Function) + */ + default MinecraftHasher sessionCast(BiFunction converter) { + return (value, encoder) -> hash(converter.apply(encoder.session(), value), encoder); + } + + /** + * Delegates to {@link MinecraftHasher#dispatch(String, Function, Function)}, uses {@code "type"} as the {@code typeKey}. + * + * @see MinecraftHasher#dispatch(String, Function, Function) + */ + default MinecraftHasher dispatch(Function typeExtractor, Function> hashDispatch) { + return dispatch("type", typeExtractor, hashDispatch); + } + + /** + * Creates a hasher that dispatches a {@link Type} from a {@link Dispatched} using {@code typeExtractor}, puts this in the {@code typeKey} key in a map, and uses a + * {@link MapBuilder} provided by {@code mapDispatch} to build the rest of the map. + * + *

This can be used to create hashers that hash an abstract type or interface into a map with different keys depending on the type.

+ * + * @param typeKey the key to store the {@link Type} in. + * @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}. + * @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}. + * @param the type of the new hasher. + */ + default MinecraftHasher dispatch(String typeKey, Function typeExtractor, Function> mapDispatch) { + return mapBuilder(builder -> builder + .accept(typeKey, this, typeExtractor) + .accept(mapDispatch, typeExtractor)); + } + + /** + * Creates a hasher that wraps the {@link Type} in a {@link Filterable}. + */ + default MinecraftHasher> filterable() { return mapBuilder(builder -> builder .accept("raw", this, Filterable::getRaw) .optionalNullable("filtered", this, Filterable::getOptional)); } - default MinecraftHasher dispatch(Function typeExtractor, Function> hashDispatch) { - return dispatch("type", typeExtractor, hashDispatch); - } - - default MinecraftHasher dispatch(String typeKey, Function typeExtractor, Function> hashDispatch) { - return mapBuilder(builder -> builder - .accept(typeKey, this, typeExtractor) - .accept(hashDispatch, typeExtractor)); - } - - default MinecraftHasher convert(Function converter) { - return (value, encoder) -> hash(converter.apply(value), encoder); - } - - default MinecraftHasher sessionConvert(BiFunction converter) { - return (value, encoder) -> hash(converter.apply(encoder.session(), value), encoder); - } - + /** + * Lazily-initialises the given hasher using {@link Suppliers#memoize(com.google.common.base.Supplier)}. + */ static MinecraftHasher lazyInitialize(Supplier> hasher) { Supplier> memoized = Suppliers.memoize(hasher::get); return (value, encoder) -> memoized.get().hash(value, encoder); } + // TODO currently unused, has to be used for mob effects, check static MinecraftHasher recursive(UnaryOperator> delegate) { return new Recursive<>(delegate); } - static > MinecraftHasher fromIdEnum(T[] values) { - return fromIdEnum(values, t -> t.name().toLowerCase()); + /** + * Uses {@link Enum#name()} (lowercased) as {@code toName} function in {@link MinecraftHasher#fromIdEnum(Enum[], Function)}. + * + *

Please be aware that you are using literal enum constants as string values here, meaning that if there is a typo in a constant, or a constant changes name, things + * may break. Use cautiously.

+ * + * @see MinecraftHasher#fromIdEnum(Enum[], Function) + */ + static > MinecraftHasher fromIdEnum(EnumConstant[] values) { + return fromIdEnum(values, constant -> constant.name().toLowerCase(Locale.ROOT)); } - static > MinecraftHasher fromIdEnum(T[] values, Function toName) { - return STRING.convert(id -> toName.apply(values[id])); + /** + * Creates a hasher that looks up an int in the array of {@link EnumConstant}s, and uses {@code toName} to turn the constant into a string, which it then hashes. + * + * @param values the array of {@link EnumConstant}s. + * @param toName the function that turns a {@link EnumConstant} into a string. + * @param the enum. + * @see MinecraftHasher#fromIdEnum(Enum[]) + */ + static > MinecraftHasher fromIdEnum(EnumConstant[] values, Function toName) { + return STRING.cast(id -> toName.apply(values[id])); } - // TODO: note that this only works correctly if enum constants are named appropriately - static > MinecraftHasher fromEnum() { - return fromEnum(t -> t.name().toLowerCase()); + /** + * Uses {@link Enum#name()} (lowercased) as {@code toName} function in {@link MinecraftHasher#fromEnum(Function)}. + * + *

Please be aware that you are using literal enum constants as string values here, meaning that if there is a typo in a constant, or a constant changes name, things + * may break. Use cautiously.

+ * + * @see MinecraftHasher#fromEnum(Function) + */ + static > MinecraftHasher fromEnum() { + return fromEnum(constant -> constant.name().toLowerCase(Locale.ROOT)); } - static > MinecraftHasher fromEnum(Function toName) { - return STRING.convert(toName); + /** + * Creates a hasher for {@link EnumConstant}s that uses {@code toName} to turn a {@link EnumConstant} into a string, which it then hashes. + * + * @param toName the function that turns a {@link EnumConstant} into a string. + * @param the enum. + * @see MinecraftHasher#fromEnum() + */ + static > MinecraftHasher fromEnum(Function toName) { + return STRING.cast(toName); } - static MinecraftHasher mapBuilder(MapBuilder builder) { + /** + * Creates a hasher that uses a {@link MapBuilder} to hash a {@link Type} into a map. + * + * @param builder the builder that creates a map from a {@link Type}. + * @param the type to hash. + */ + static MinecraftHasher mapBuilder(MapBuilder builder) { return (value, encoder) -> builder.apply(new MapHasher<>(value, encoder)).build(); } + /** + * Creates a hasher that hashes {@link K} keys and {@link V} values into a map, using the {@code keyHasher} and {@code valueHasher} respectively. + * + *

Note that the key hasher is usually expected to hash into a string, but this is not necessarily a requirement. It may be once different encoders are used to encode to NBT or JSON, + * but this is not the case yet.

+ * + * @param keyHasher the hasher that hashes objects of type {@link K}. + * @param valueHasher the hasher that hashes objects of type {@link V}. + * @param the key type. + * @param the value type. + */ static MinecraftHasher> map(MinecraftHasher keyHasher, MinecraftHasher valueHasher) { return (map, encoder) -> encoder.map(map.entrySet().stream() .map(entry -> Map.entry(keyHasher.hash(entry.getKey(), encoder), valueHasher.hash(entry.getValue(), encoder))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } - static MinecraftHasher either(MinecraftHasher firstHasher, Function firstExtractor, MinecraftHasher secondHasher, Function secondExtractor) { + /** + * Creates a hasher that hashes {@link Type} using either {@code firstHasher} to hash {@link First} (obtained using {@code firstExtractor}), or uses {@code secondHasher} + * to hash {@link Second} (obtained via {@code secondExtractor}). + * + *

Specifically, the hasher first tries to use {@code firstExtractor} to obtain a {@link First} from {@link Type}. If this returns a not-null value, {@code firstHasher} is used to hash the value.

+ * + *

If the returned value is null, then {@code secondExtractor} is used to obtain a {@link Second} from {@link Type}. This must never be null. {@code secondHasher} is then used to hash the value.

+ * + *

Note: {@code secondExtractor} must never return null if {@code firstExtractor} does!

+ * + * @param firstHasher the hasher used to hash {@link First}. + * @param firstExtractor the function used to obtain a {@link First} from a {@link Type}. + * @param secondHasher the hasher used to hash {@link Second}. + * @param secondExtractor the function used to obtain a {@link Second} from a {@link Type}. + * @param the type to hash. + * @param the first either type to hash. + * @param the second either type to hash. + */ + static MinecraftHasher either(MinecraftHasher firstHasher, Function firstExtractor, + MinecraftHasher secondHasher, Function secondExtractor) { return (value, encoder) -> { - F first = firstExtractor.apply(value); + First first = firstExtractor.apply(value); if (first != null) { return firstHasher.hash(first, encoder); } @@ -216,7 +351,13 @@ public interface MinecraftHasher { }; } - static MinecraftHasher dispatch(Function> hashDispatch) { + /** + * Creates a hasher that dispatches a {@link MinecraftHasher} from a {@link Type}, and then uses that hasher to encode {@link Type}. + * + * @param hashDispatch the function that returns a {@link MinecraftHasher} from a {@link Type}. + * @param the type to hash. + */ + static MinecraftHasher dispatch(Function> hashDispatch) { return (value, encoder) -> hashDispatch.apply(value).hash(value, encoder); } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java index a0ddf7504..1ed42bb70 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/RegistryHasher.java @@ -29,6 +29,7 @@ import net.kyori.adventure.key.Key; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.hashing.data.ConsumeEffectType; +import org.geysermc.geyser.item.hashing.data.FireworkExplosionShape; import org.geysermc.geyser.item.hashing.data.ItemContainerSlot; import org.geysermc.geyser.item.hashing.data.entity.AxolotlVariant; import org.geysermc.geyser.item.hashing.data.entity.FoxVariant; @@ -55,6 +56,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BeehiveOccupant; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlocksAttacks; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ConsumeEffect; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; @@ -94,7 +96,7 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher DAMAGE_TYPE = registry(JavaRegistries.DAMAGE_TYPE); - MinecraftHasher> DATA_COMPONENT_TYPE = KEY.convert(DataComponentType::getKey); + MinecraftHasher> DATA_COMPONENT_TYPE = KEY.cast(DataComponentType::getKey); @SuppressWarnings({"unchecked", "rawtypes"}) // Java generics :( MinecraftHasher> DATA_COMPONENT = (component, encoder) -> { @@ -102,7 +104,7 @@ public interface RegistryHasher extends MinecraftHasher { return hasher.hash(component.getValue(), encoder); }; - MinecraftHasher DATA_COMPONENTS = MinecraftHasher.map(RegistryHasher.DATA_COMPONENT_TYPE, DATA_COMPONENT).convert(DataComponents::getDataComponents); // TODO component removals (needs unit value and ! component prefix) + MinecraftHasher DATA_COMPONENTS = MinecraftHasher.map(RegistryHasher.DATA_COMPONENT_TYPE, DATA_COMPONENT).cast(DataComponents::getDataComponents); // TODO component removals (needs unit value and ! component prefix) MinecraftHasher ITEM_STACK = MinecraftHasher.mapBuilder(builder -> builder .accept("id", ITEM, ItemStack::getId) @@ -113,7 +115,7 @@ public interface RegistryHasher extends MinecraftHasher { .accept("slot", INT, ItemContainerSlot::index) .accept("item", ITEM_STACK, ItemContainerSlot::item)); - MinecraftHasher> ITEM_CONTAINER_CONTENTS = CONTAINER_SLOT.list().convert(stacks -> { + MinecraftHasher> ITEM_CONTAINER_CONTENTS = CONTAINER_SLOT.list().cast(stacks -> { List slots = new ArrayList<>(); for (int i = 0; i < stacks.size(); i++) { slots.add(new ItemContainerSlot(i, stacks.get(i))); @@ -139,7 +141,7 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher POTION = enumIdRegistry(Potion.values()); - MinecraftHasher BUILTIN_SOUND = KEY.convert(sound -> MinecraftKey.key(sound.getName())); + MinecraftHasher BUILTIN_SOUND = KEY.cast(sound -> MinecraftKey.key(sound.getName())); MinecraftHasher CUSTOM_SOUND = MinecraftHasher.mapBuilder(builder -> builder .accept("sound_id", KEY, sound -> MinecraftKey.key(sound.getName())) @@ -152,7 +154,7 @@ public interface RegistryHasher extends MinecraftHasher { return CUSTOM_SOUND.hash((CustomSound) sound, encoder); }; - MinecraftHasher ITEM_ENCHANTMENTS = MinecraftHasher.map(RegistryHasher.ENCHANTMENT, MinecraftHasher.INT).convert(ItemEnchantments::getEnchantments); + MinecraftHasher ITEM_ENCHANTMENTS = MinecraftHasher.map(RegistryHasher.ENCHANTMENT, MinecraftHasher.INT).cast(ItemEnchantments::getEnchantments); MinecraftHasher BLOCK_PREDICATE = MinecraftHasher.mapBuilder(builder -> builder .optionalNullable("blocks", BLOCK.holderSet(), AdventureModePredicate.BlockPredicate::getBlocks) @@ -175,6 +177,8 @@ public interface RegistryHasher extends MinecraftHasher { .accept("operation", ATTRIBUTE_MODIFIER_OPERATION, entry -> entry.getModifier().getOperation()) .optional("slot", EQUIPMENT_SLOT_GROUP, ItemAttributeModifiers.Entry::getSlot, ItemAttributeModifiers.EquipmentSlotGroup.ANY)); + MinecraftHasher ITEM_USE_ANIMATION = MinecraftHasher.fromEnum(); + MinecraftHasher CONSUME_EFFECT_TYPE = enumRegistry(); MinecraftHasher CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, type -> type.getBuilder().cast()); @@ -247,8 +251,10 @@ public interface RegistryHasher extends MinecraftHasher { .accept("pattern", BANNER_PATTERN, BannerPatternLayer::getPattern) .accept("color", DYE_COLOR, BannerPatternLayer::getColorId)); + MinecraftHasher FIREWORK_EXPLOSION_SHAPE = MinecraftHasher.fromIdEnum(FireworkExplosionShape.values()); + MinecraftHasher FIREWORK_EXPLOSION = MinecraftHasher.mapBuilder(builder -> builder - .accept("shape", MinecraftHasher.FIREWORK_EXPLOSION_SHAPE, Fireworks.FireworkExplosion::getShapeId) + .accept("shape", FIREWORK_EXPLOSION_SHAPE, Fireworks.FireworkExplosion::getShapeId) .optionalList("colors", INT, explosion -> IntStream.of(explosion.getColors()).boxed().toList()) .optionalList("fade_colors", INT, explosion -> IntStream.of(explosion.getFadeColors()).boxed().toList()) .optional("has_trail", BOOL, Fireworks.FireworkExplosion::isHasTrail, false) @@ -271,11 +277,11 @@ public interface RegistryHasher extends MinecraftHasher { MinecraftHasher PARROT_VARIANT = MinecraftHasher.fromIdEnum(ParrotVariant.values()); - MinecraftHasher TROPICAL_FISH_PATTERN = MinecraftHasher.fromEnum().convert(TropicalFishPattern::fromPackedId); + MinecraftHasher TROPICAL_FISH_PATTERN = MinecraftHasher.fromEnum().cast(TropicalFishPattern::fromPackedId); MinecraftHasher MOOSHROOM_VARIANT = MinecraftHasher.fromIdEnum(MooshroomVariant.values()); - MinecraftHasher RABBIT_VARIANT = MinecraftHasher.fromEnum().convert(RabbitVariant::fromId); + MinecraftHasher RABBIT_VARIANT = MinecraftHasher.fromEnum().cast(RabbitVariant::fromId); RegistryHasher PIG_VARIANT = registry(JavaRegistries.PIG_VARIANT); @@ -301,7 +307,7 @@ public interface RegistryHasher extends MinecraftHasher { RegistryHasher CAT_VARIANT = registry(JavaRegistries.CAT_VARIANT); static RegistryHasher registry(JavaRegistryKey registry) { - MinecraftHasher hasher = KEY.sessionConvert(registry::keyFromNetworkId); + MinecraftHasher hasher = KEY.sessionCast(registry::keyFromNetworkId); return hasher::hash; } @@ -318,7 +324,7 @@ public interface RegistryHasher extends MinecraftHasher { // TODO note that this only works if the enum constants match static > MinecraftHasher enumRegistry() { - return KEY.convert(t -> MinecraftKey.key(t.name().toLowerCase())); + return KEY.cast(t -> MinecraftKey.key(t.name().toLowerCase())); } static > RegistryHasher enumIdRegistry(T[] values) { @@ -326,7 +332,7 @@ public interface RegistryHasher extends MinecraftHasher { } static > RegistryHasher enumIdRegistry(T[] values, Function toKey) { - MinecraftHasher hasher = KEY.convert(i -> toKey.apply(values[i])); + MinecraftHasher hasher = KEY.cast(i -> toKey.apply(values[i])); return hasher::hash; }