1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-06 15:41:50 +00:00

Item stack, use remainder component hashers, update mappings

This commit is contained in:
Eclipse
2025-03-25 21:29:28 +00:00
parent e78b6b431e
commit ba9544713a
4 changed files with 77 additions and 25 deletions

View File

@@ -38,10 +38,12 @@ import org.geysermc.geyser.util.MinecraftKey;
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.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.CustomModelData;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.FoodProperties;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
@@ -112,7 +114,7 @@ public class ComponentHashers {
.optional("sound", RegistryHasher.SOUND_EVENT, Consumable::sound, BuiltinSound.ENTITY_GENERIC_EAT)
.optional("has_consume_particles", MinecraftHasher.BOOL, Consumable::hasConsumeParticles, true)); // TODO consume effect needs identifier in MCPL
// TODO use remainder needs item stack codec, recursion go brr
register(DataComponentTypes.USE_REMAINDER, RegistryHasher.ITEM_STACK);
registerMap(DataComponentTypes.USE_COOLDOWN, builder -> builder
.accept("seconds", MinecraftHasher.FLOAT, UseCooldown::seconds)
@@ -120,7 +122,7 @@ public class ComponentHashers {
registerMap(DataComponentTypes.DAMAGE_RESISTANT, builder -> builder
.accept("types", MinecraftHasher.TAG, Function.identity()));
registerMap(DataComponentTypes.TOOL, builder -> builder
.acceptList("rules", MinecraftHasher.TOOL_RULE, ToolData::getRules)
.acceptList("rules", RegistryHasher.TOOL_RULE, ToolData::getRules)
.optional("default_mining_speed", MinecraftHasher.FLOAT, ToolData::getDefaultMiningSpeed, 1.0F)
.optional("damage_per_block", MinecraftHasher.INT, ToolData::getDamagePerBlock, 1)
.optional("can_destroy_blocks_in_creative", MinecraftHasher.BOOL, ToolData::isCanDestroyBlocksInCreative, true));
@@ -160,7 +162,7 @@ public class ComponentHashers {
registerMap(DataComponentTypes.POTION_CONTENTS, builder -> builder
.optional("potion", RegistryHasher.POTION, PotionContents::getPotionId, -1)
.optional("custom_color", MinecraftHasher.INT, PotionContents::getCustomColor, -1)
.optionalList("custom_effects", MinecraftHasher.MOB_EFFECT_INSTANCE, PotionContents::getCustomEffects)
.optionalList("custom_effects", RegistryHasher.MOB_EFFECT_INSTANCE, PotionContents::getCustomEffects)
.optionalNullable("custom_name", MinecraftHasher.STRING, PotionContents::getCustomName));
register(DataComponentTypes.POTION_DURATION_SCALE, MinecraftHasher.FLOAT);
@@ -185,10 +187,18 @@ public class ComponentHashers {
hashers.put(component, hasher);
}
public static <T> MinecraftHasher<T> hasherOrEmpty(DataComponentType<T> component) {
MinecraftHasher<T> hasher = (MinecraftHasher<T>) hashers.get(component);
if (hasher == null) {
return MinecraftHasher.UNIT.convert(value -> Unit.INSTANCE);
}
return hasher;
}
public static <T> HashCode hash(GeyserSession session, DataComponentType<T> component, T value) {
MinecraftHasher<T> hasher = (MinecraftHasher<T>) hashers.get(component);
if (hasher == null) {
throw new IllegalStateException("Unregistered hasher for component " + component + "!");
throw new IllegalStateException("Unregistered hasher for component " + component + "!"); // TODO we might not have hashers for every component, in which case, fix this
}
return hasher.hash(value, new MinecraftHashEncoder(session));
}
@@ -241,6 +251,13 @@ public class ComponentHashers {
testHash(session, DataComponentTypes.FOOD, new FoodProperties(3, 5.7F, true), 1917653498);
testHash(session, DataComponentTypes.FOOD, new FoodProperties(7, 0.15F, false), -184166204);
testHash(session, DataComponentTypes.USE_REMAINDER, new ItemStack(Items.MELON.javaId(), 52), -1279684916);
DataComponents specialComponents = new DataComponents(new HashMap<>());
specialComponents.put(DataComponentTypes.ITEM_MODEL, MinecraftKey.key("testing"));
specialComponents.put(DataComponentTypes.MAX_STACK_SIZE, 44);
testHash(session, DataComponentTypes.USE_REMAINDER, new ItemStack(Items.PUMPKIN.javaId(), 32, specialComponents), 1991032843);
testHash(session, DataComponentTypes.DAMAGE_RESISTANT, MinecraftKey.key("testing"), -1230493835);
testHash(session, DataComponentTypes.TOOL, new ToolData(List.of(), 5.0F, 3, false), -1789071928);

View File

@@ -25,6 +25,7 @@
package org.geysermc.geyser.item.hashing;
import com.google.common.base.Suppliers;
import com.google.common.hash.HashCode;
import net.kyori.adventure.key.Key;
import org.cloudburstmc.nbt.NbtMap;
@@ -32,14 +33,13 @@ import org.geysermc.geyser.item.components.Rarity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
@@ -75,21 +75,8 @@ public interface MinecraftHasher<T> {
MinecraftHasher<Consumable.ItemUseAnimation> ITEM_USE_ANIMATION = fromEnum();
MinecraftHasher<ToolData.Rule> TOOL_RULE = mapBuilder(builder -> builder
.accept("blocks", RegistryHasher.BLOCK.holderSet(), ToolData.Rule::getBlocks)
.optionalNullable("speed", MinecraftHasher.FLOAT, ToolData.Rule::getSpeed)
.optionalNullable("correct_for_drops", MinecraftHasher.BOOL, ToolData.Rule::getCorrectForDrops));
MinecraftHasher<EquipmentSlot> EQUIPMENT_SLOT = fromEnum(); // FIXME MCPL enum constants aren't right
MinecraftHasher<MobEffectInstance> MOB_EFFECT_INSTANCE = mapBuilder(builder -> builder
.accept("id", RegistryHasher.EFFECT, MobEffectInstance::getEffect)
.optional("amplifier", BYTE, instance -> (byte) instance.getDetails().getAmplifier(), (byte) 0)
.optional("duration", INT, instance -> instance.getDetails().getDuration(), 0)
.optional("ambient", BOOL, instance -> instance.getDetails().isAmbient(), false)
.optional("show_particles", BOOL, instance -> instance.getDetails().isShowParticles(), true)
.accept("show_icon", BOOL, instance -> instance.getDetails().isShowIcon())); // TODO check this, also hidden effect but is recursive
HashCode hash(T value, MinecraftHashEncoder encoder);
default MinecraftHasher<List<T>> list() {
@@ -97,11 +84,15 @@ public interface MinecraftHasher<T> {
}
default <F> MinecraftHasher<F> convert(Function<F, T> converter) {
return (object, encoder) -> hash(converter.apply(object), encoder);
return (value, encoder) -> hash(converter.apply(value), encoder);
}
default <F> MinecraftHasher<F> sessionConvert(BiFunction<GeyserSession, F, T> converter) {
return (object, encoder) -> hash(converter.apply(encoder.session(), object), encoder);
return (value, encoder) -> hash(converter.apply(encoder.session(), value), encoder);
}
static <T> MinecraftHasher<T> recursive(UnaryOperator<MinecraftHasher<T>> delegate) {
return new Recursive<>(delegate);
}
static <T extends Enum<T>> MinecraftHasher<Integer> fromIdEnum(T[] values, Function<T, String> toName) {
@@ -114,7 +105,7 @@ public interface MinecraftHasher<T> {
}
static <T> MinecraftHasher<T> mapBuilder(UnaryOperator<MapHasher<T>> builder) {
return (object, encoder) -> builder.apply(new MapHasher<>(object, encoder)).build();
return (value, encoder) -> builder.apply(new MapHasher<>(value, encoder)).build();
}
static <K, V> MinecraftHasher<Map<K, V>> map(MinecraftHasher<K> keyHasher, MinecraftHasher<V> valueHasher) {
@@ -122,4 +113,17 @@ public interface MinecraftHasher<T> {
.map(entry -> Map.entry(keyHasher.hash(entry.getKey(), encoder), valueHasher.hash(entry.getValue(), encoder)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
class Recursive<T> implements MinecraftHasher<T> {
private final Supplier<MinecraftHasher<T>> delegate;
public Recursive(UnaryOperator<MinecraftHasher<T>> delegate) {
this.delegate = Suppliers.memoize(() -> delegate.apply(this));
}
@Override
public HashCode hash(T value, MinecraftHashEncoder encoder) {
return delegate.get().hash(value, encoder);
}
}
}

View File

@@ -31,8 +31,13 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
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.DataComponent;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.CustomSound;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.Sound;
@@ -49,11 +54,32 @@ public interface RegistryHasher extends MinecraftHasher<Integer> {
RegistryHasher ENCHANTMENT = registry(JavaRegistries.ENCHANTMENT);
MinecraftHasher<DataComponentType<?>> DATA_COMPONENT_TYPE = KEY.convert(DataComponentType::getKey);
@SuppressWarnings({"unchecked", "rawtypes"}) // Java generics :(
MinecraftHasher<DataComponent<?, ?>> DATA_COMPONENT = (component, encoder) -> {
MinecraftHasher hasher = ComponentHashers.hasherOrEmpty(component.getType());
return hasher.hash(component.getValue(), encoder);
};
MinecraftHasher<DataComponents> DATA_COMPONENTS = MinecraftHasher.map(RegistryHasher.DATA_COMPONENT_TYPE, DATA_COMPONENT).convert(DataComponents::getDataComponents); // TODO component removals (needs unit value and ! component prefix)
MinecraftHasher<ItemStack> ITEM_STACK = MinecraftHasher.mapBuilder(builder -> builder
.accept("id", ITEM, ItemStack::getId)
.accept("count", INT, ItemStack::getAmount)
.optionalNullable("components", DATA_COMPONENTS, ItemStack::getDataComponentsPatch));
MinecraftHasher<Effect> EFFECT = enumRegistry();
RegistryHasher POTION = enumIdRegistry(Potion.values());
MinecraftHasher<MobEffectInstance> MOB_EFFECT_INSTANCE = MinecraftHasher.mapBuilder(builder -> builder
.accept("id", RegistryHasher.EFFECT, MobEffectInstance::getEffect)
.optional("amplifier", BYTE, instance -> (byte) instance.getDetails().getAmplifier(), (byte) 0)
.optional("duration", INT, instance -> instance.getDetails().getDuration(), 0)
.optional("ambient", BOOL, instance -> instance.getDetails().isAmbient(), false)
.optional("show_particles", BOOL, instance -> instance.getDetails().isShowParticles(), true)
.accept("show_icon", BOOL, instance -> instance.getDetails().isShowIcon())); // TODO check this, also hidden effect but is recursive
MinecraftHasher<DataComponentType<?>> DATA_COMPONENT_TYPE = KEY.convert(DataComponentType::getKey);
RegistryHasher POTION = enumIdRegistry(Potion.values());
MinecraftHasher<BuiltinSound> BUILTIN_SOUND = KEY.convert(sound -> MinecraftKey.key(sound.getName()));
@@ -68,6 +94,11 @@ public interface RegistryHasher extends MinecraftHasher<Integer> {
return CUSTOM_SOUND.hash((CustomSound) sound, encoder);
};
MinecraftHasher<ToolData.Rule> TOOL_RULE = MinecraftHasher.mapBuilder(builder -> builder
.accept("blocks", RegistryHasher.BLOCK.holderSet(), ToolData.Rule::getBlocks)
.optionalNullable("speed", MinecraftHasher.FLOAT, ToolData.Rule::getSpeed)
.optionalNullable("correct_for_drops", MinecraftHasher.BOOL, ToolData.Rule::getCorrectForDrops));
static RegistryHasher registry(JavaRegistryKey<?> registry) {
MinecraftHasher<Integer> hasher = KEY.sessionConvert(registry::keyFromNetworkId);
return hasher::hash;