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

Update data component hashers for 1.21.6's changes

This commit is contained in:
Eclipse
2025-06-13 17:09:39 +00:00
parent 35ef08e5bb
commit ef2cdd0e74
8 changed files with 121 additions and 35 deletions

View File

@@ -35,6 +35,7 @@ import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.ShadowColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
@@ -96,9 +97,9 @@ public interface ComponentHasher {
.optionalNullable("name", COMPONENT, event -> ((HoverEvent.ShowEntity) event.value()).name());
});
// TODO shadow colours - needs kyori bump
MapBuilder<Style> STYLE = builder -> builder
.optionalNullable("color", COLOR, Style::color)
.optionalNullable("shadow_color", MinecraftHasher.INT.cast(ShadowColor::value), Style::shadowColor)
.optional("bold", DECORATION_STATE, style -> style.decoration(TextDecoration.BOLD), TextDecoration.State.NOT_SET)
.optional("italic", DECORATION_STATE, style -> style.decoration(TextDecoration.ITALIC), TextDecoration.State.NOT_SET)
.optional("underlined", DECORATION_STATE, style -> style.decoration(TextDecoration.UNDERLINED), TextDecoration.State.NOT_SET)

View File

@@ -43,8 +43,13 @@ import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.item.HashedStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlockStateProperties;
@@ -75,6 +80,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.Weapon;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.WritableBookContent;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.WrittenBookContent;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.CustomSound;
import java.util.HashMap;
import java.util.HashSet;
@@ -158,7 +164,9 @@ public class DataComponentHashers {
.optional("dispensable", MinecraftHasher.BOOL, Equippable::dispensable, true)
.optional("swappable", MinecraftHasher.BOOL, Equippable::swappable, true)
.optional("damage_on_hurt", MinecraftHasher.BOOL, Equippable::damageOnHurt, true)
.optional("equip_on_interact", MinecraftHasher.BOOL, Equippable::equipOnInteract, false));
.optional("equip_on_interact", MinecraftHasher.BOOL, Equippable::equipOnInteract, false)
.optional("can_be_sheared", MinecraftHasher.BOOL, Equippable::canBeSheared, false)
.optional("shearing_sound", RegistryHasher.SOUND_EVENT, Equippable::shearingSound, BuiltinSound.ITEM_SHEARS_SNIP));
registerMap(DataComponentTypes.REPAIRABLE, builder -> builder
.accept("items", RegistryHasher.ITEM.holderSet(), Function.identity()));
@@ -258,7 +266,7 @@ public class DataComponentHashers {
.sessionCast((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.holder());
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.
register(DataComponentTypes.LLAMA_VARIANT, RegistryHasher.LLAMA_VARIANT);
register(DataComponentTypes.AXOLOTL_VARIANT, RegistryHasher.AXOLOTL_VARIANT);
register(DataComponentTypes.CAT_VARIANT, RegistryHasher.CAT_VARIANT);
@@ -367,6 +375,56 @@ public class DataComponentHashers {
0, 1
)), 0); // TODO identifier lookup
testHash(session, DataComponentTypes.ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers(
List.of(
ItemAttributeModifiers.Entry.builder()
.attribute(AttributeType.Builtin.ATTACK_DAMAGE.getId())
.modifier(ItemAttributeModifiers.AttributeModifier.builder()
.id(MinecraftKey.key("test_modifier_1"))
.amount(2.0)
.operation(ModifierOperation.ADD)
.build())
.slot(ItemAttributeModifiers.EquipmentSlotGroup.ANY)
.display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.DEFAULT, null))
.build(),
ItemAttributeModifiers.Entry.builder()
.attribute(AttributeType.Builtin.JUMP_STRENGTH.getId())
.modifier(ItemAttributeModifiers.AttributeModifier.builder()
.id(MinecraftKey.key("test_modifier_2"))
.amount(4.2)
.operation(ModifierOperation.ADD_MULTIPLIED_TOTAL)
.build())
.slot(ItemAttributeModifiers.EquipmentSlotGroup.HEAD)
.display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.HIDDEN, null))
.build(),
ItemAttributeModifiers.Entry.builder()
.attribute(AttributeType.Builtin.WAYPOINT_RECEIVE_RANGE.getId())
.modifier(ItemAttributeModifiers.AttributeModifier.builder()
.id(MinecraftKey.key("geyser_mc:test_modifier_3"))
.amount(5.4)
.operation(ModifierOperation.ADD_MULTIPLIED_BASE)
.build())
.slot(ItemAttributeModifiers.EquipmentSlotGroup.FEET)
.display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.DEFAULT, null))
.build()
)
), 1889444548);
testHash(session, DataComponentTypes.ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers(
List.of(
ItemAttributeModifiers.Entry.builder()
.attribute(AttributeType.Builtin.WAYPOINT_TRANSMIT_RANGE.getId())
.modifier(ItemAttributeModifiers.AttributeModifier.builder()
.id(MinecraftKey.key("geyser_mc:test_modifier_4"))
.amount(2.0)
.operation(ModifierOperation.ADD)
.build())
.slot(ItemAttributeModifiers.EquipmentSlotGroup.ANY)
.display(new ItemAttributeModifiers.Display(ItemAttributeModifiers.DisplayType.OVERRIDE, Component.text("give me a test")))
.build()
)
), 1375953017);
testHash(session, DataComponentTypes.CUSTOM_MODEL_DATA,
new CustomModelData(List.of(5.0F, 3.0F, -1.0F), List.of(false, true, false), List.of("1", "3", "2"), List.of(3424, -123, 345)), 1947635619);
@@ -411,18 +469,25 @@ public class DataComponentHashers {
testHash(session, DataComponentTypes.ENCHANTABLE, 3, -1834983819);
// TODO
// testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null,
// true, true, true, false), 1294431019);
// testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_CHAIN, MinecraftKey.key("testing"), null, null,
// true, true, true, false), 1226203061);
// testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.AMBIENT_CAVE, null, null, null,
// false, true, false, false), 1416408052);
// testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ENTITY_BREEZE_WIND_BURST, null, MinecraftKey.key("testing"),
// new HolderSet(new int[]{EntityType.ACACIA_BOAT.ordinal()}), false, true, false, false), 1711275245);
//
// testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.HELMET, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null,
// true, true, true, false), 497790992); // TODO broken because equipment slot names don't match
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null,
true, true, true, false,
false, BuiltinSound.ITEM_SHEARS_SNIP), 1294431019);
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ITEM_ARMOR_EQUIP_CHAIN, MinecraftKey.key("testing"), null, null,
true, true, true, false,
true, BuiltinSound.ITEM_BONE_MEAL_USE), -801616214);
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.AMBIENT_CAVE, null, null, null,
false, true, false, false,
false, new CustomSound("testing_equippable", false, 10.0F)), -1145684769);
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.BODY, BuiltinSound.ENTITY_BREEZE_WIND_BURST, null, MinecraftKey.key("testing"),
new HolderSet(new int[]{EntityType.ACACIA_BOAT.ordinal()}), false, true, false, false,
true, BuiltinSound.BLOCK_NETHERITE_BLOCK_PLACE), -115079770);
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.HELMET, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null,
true, true, true, false,
false, BuiltinSound.ITEM_SHEARS_SNIP), 497790992);
testHash(session, DataComponentTypes.EQUIPPABLE, new Equippable(EquipmentSlot.HELMET, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null,
new HolderSet(MinecraftKey.key("aquatic")),
true, true, true, false,
false, BuiltinSound.ITEM_SHEARS_SNIP), 264760955);
testHash(session, DataComponentTypes.REPAIRABLE, new HolderSet(new int[]{Items.AMETHYST_BLOCK.javaId(), Items.PUMPKIN.javaId()}), -36715567);

View File

@@ -49,7 +49,7 @@ public interface MapBuilder<Type> extends UnaryOperator<MapHasher<Type>> {
*
* @param <Type> the type to encode.
*/
static <Type> MapBuilder<Type> empty() {
static <Type> MapBuilder<Type> unit() {
return builder -> builder;
}
}

View File

@@ -30,8 +30,10 @@ import com.google.common.hash.HashCode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* {@link MapHasher}s are used to encode a {@link Type} to a map-like structure, which is then hashed using a {@link MinecraftHashEncoder}.
@@ -135,11 +137,7 @@ public class MapHasher<Type> {
* @param <Value> the type of the value.
*/
public <Value> MapHasher<Type> optionalNullable(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor) {
Value value = extractor.apply(object);
if (value != null) {
acceptConstant(key, hasher, value);
}
return this;
return optionalPredicate(key, hasher, extractor, Objects::nonNull);
}
/**
@@ -166,8 +164,21 @@ public class MapHasher<Type> {
* @param <Value> the type of the value.
*/
public <Value> MapHasher<Type> optional(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor, Value defaultValue) {
return optionalPredicate(key, hasher, extractor, value -> !value.equals(defaultValue));
}
/**
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map only if {@code predicate} returns {@code true} for it.
*
* @param key the key to put the {@link Value} in.
* @param hasher the hasher used to hash a {@link Value}.
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
* @param predicate the predicate that checks if a {@link Value} should be added to the map. The {@link Value} won't be added to the map if the predicate returns {@code false} for it.
* @param <Value> the type of the value.
*/
public <Value> MapHasher<Type> optionalPredicate(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor, Predicate<Value> predicate) {
Value value = extractor.apply(object);
if (!value.equals(defaultValue)) {
if (predicate.test(value)) {
acceptConstant(key, hasher, value);
}
return this;

View File

@@ -88,7 +88,7 @@ import java.util.stream.IntStream;
@FunctionalInterface
public interface MinecraftHasher<Type> {
MinecraftHasher<Unit> UNIT = (unit, encoder) -> encoder.emptyMap();
MinecraftHasher<Unit> UNIT = unit();
MinecraftHasher<Byte> BYTE = (b, encoder) -> encoder.number(b);
@@ -237,6 +237,13 @@ public interface MinecraftHasher<Type> {
.optionalNullable("filtered", this, Filterable::getOptional));
}
/**
* Creates a hasher that always encodes into an empty map.
*/
static <Type> MinecraftHasher<Type> unit() {
return (value, encoder) -> encoder.emptyMap();
}
/**
* Lazily-initialises the given hasher using {@link Suppliers#memoize(com.google.common.base.Supplier)}.
*/

View File

@@ -50,7 +50,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.PaintingVariant;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.AdventureModePredicate;
@@ -189,14 +188,7 @@ public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
RegistryHasher<?> FROG_VARIANT = registry(JavaRegistries.FROG_VARIANT);
MinecraftHasher<PaintingVariant> DIRECT_PAINTING_VARIANT = MinecraftHasher.mapBuilder(builder -> builder
.accept("width", INT, PaintingVariant::width)
.accept("height", INT, PaintingVariant::height)
.accept("asset_id", KEY, PaintingVariant::assetId)
.optionalNullable("title", ComponentHasher.COMPONENT, PaintingVariant::title)
.optionalNullable("author", ComponentHasher.COMPONENT, PaintingVariant::author));
RegistryHasher<PaintingVariant> PAINTING_VARIANT = registry(JavaRegistries.PAINTING_VARIANT, DIRECT_PAINTING_VARIANT);
RegistryHasher<?> PAINTING_VARIANT = registry(JavaRegistries.PAINTING_VARIANT);
RegistryHasher<?> CAT_VARIANT = registry(JavaRegistries.CAT_VARIANT);
@@ -287,12 +279,22 @@ public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
MinecraftHasher<AdventureModePredicate> ADVENTURE_MODE_PREDICATE = MinecraftHasher.either(BLOCK_PREDICATE,
predicate -> predicate.getPredicates().size() == 1 ? predicate.getPredicates().get(0) : null, BLOCK_PREDICATE.list(), AdventureModePredicate::getPredicates);
MinecraftHasher<ItemAttributeModifiers.DisplayType> ATTRIBUTE_MODIFIER_DISPLAY_TYPE = MinecraftHasher.fromEnum();
MinecraftHasher<ItemAttributeModifiers.Display> ATTRIBUTE_MODIFIER_DISPLAY = ATTRIBUTE_MODIFIER_DISPLAY_TYPE.dispatch(ItemAttributeModifiers.Display::getType,
displayType -> switch (displayType) {
case DEFAULT, HIDDEN -> MapBuilder.unit();
case OVERRIDE -> builder -> builder
.accept("value", ComponentHasher.COMPONENT, ItemAttributeModifiers.Display::getComponent);
});
MinecraftHasher<ItemAttributeModifiers.Entry> ATTRIBUTE_MODIFIER_ENTRY = MinecraftHasher.mapBuilder(builder -> builder
.accept("type", RegistryHasher.ATTRIBUTE, ItemAttributeModifiers.Entry::getAttribute)
.accept("id", KEY, entry -> entry.getModifier().getId())
.accept("amount", DOUBLE, entry -> entry.getModifier().getAmount())
.accept("operation", ATTRIBUTE_MODIFIER_OPERATION, entry -> entry.getModifier().getOperation())
.optional("slot", EQUIPMENT_SLOT_GROUP, ItemAttributeModifiers.Entry::getSlot, ItemAttributeModifiers.EquipmentSlotGroup.ANY));
.optional("slot", EQUIPMENT_SLOT_GROUP, ItemAttributeModifiers.Entry::getSlot, ItemAttributeModifiers.EquipmentSlotGroup.ANY)
.optionalPredicate("display", ATTRIBUTE_MODIFIER_DISPLAY, ItemAttributeModifiers.Entry::getDisplay, display -> display.getType() != ItemAttributeModifiers.DisplayType.DEFAULT));
MinecraftHasher<Consumable.ItemUseAnimation> ITEM_USE_ANIMATION = MinecraftHasher.fromEnum();

View File

@@ -49,7 +49,7 @@ public enum ConsumeEffectType {
<T extends ConsumeEffect> ConsumeEffectType(Class<T> clazz) {
this.clazz = clazz;
this.builder = MapBuilder.empty();
this.builder = MapBuilder.unit();
}
<T extends ConsumeEffect> ConsumeEffectType(Class<T> clazz, MapBuilder<T> builder) {

View File

@@ -15,7 +15,7 @@ protocol-codec = "3.0.0.Beta6-20250506.012145-17"
raknet = "1.0.0.CR3-20250218.160705-18"
minecraftauth = "4.1.1"
mcprotocollib = "1.21.6-SNAPSHOT"
adventure = "4.14.0"
adventure = "4.21.0"
adventure-platform = "4.3.0"
junit = "5.9.2"
checkerframework = "3.19.0"