From 8908cce24ffae77969e88639568bd71dcb3d1503 Mon Sep 17 00:00:00 2001 From: booky10 Date: Mon, 1 Jul 2024 21:06:59 +0200 Subject: [PATCH] Implement enchantment effect component types --- api/build.gradle.kts | 2 + .../protocol/component/ComponentType.java | 20 +++ .../protocol/component/ComponentTypes.java | 90 ++++--------- .../EnchantEffectComponentTypes.java | 120 ++++++++++++++++++ .../protocol/component/IComponentMap.java | 31 +++++ .../component/StaticComponentType.java | 94 ++++++++++++++ .../enchantment/type/EnchantmentType.java | 12 +- .../type/StaticEnchantmentType.java | 9 +- .../enchantment/effect_component_type.json | 34 +++++ 9 files changed, 340 insertions(+), 72 deletions(-) create mode 100644 api/src/main/java/com/github/retrooper/packetevents/protocol/component/EnchantEffectComponentTypes.java create mode 100644 api/src/main/java/com/github/retrooper/packetevents/protocol/component/StaticComponentType.java create mode 100644 mappings/enchantment/effect_component_type.json diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 9a2b73c750..f228bf4872 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -62,6 +62,8 @@ mappingCompression { compress("command/argument_parser_mappings.json") + compress("enchantment/effect_component_type.json") + compress("entity/entity_data_type_mappings.json") compress("entity/painting_mappings.json") diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentType.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentType.java index 47bbf2211f..a823893c09 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentType.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentType.java @@ -19,6 +19,8 @@ package com.github.retrooper.packetevents.protocol.component; import com.github.retrooper.packetevents.protocol.mapper.MappedEntity; +import com.github.retrooper.packetevents.protocol.nbt.NBT; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.wrapper.PacketWrapper; public interface ComponentType extends MappedEntity { @@ -26,4 +28,22 @@ public interface ComponentType extends MappedEntity { T read(PacketWrapper wrapper); void write(PacketWrapper wrapper, T content); + + default T decode(NBT nbt, ClientVersion version) { + throw new UnsupportedOperationException(); + } + + default NBT encode(T value, ClientVersion version) { + throw new UnsupportedOperationException(); + } + + interface Decoder { + + T decode(NBT nbt, ClientVersion version); + } + + interface Encoder { + + NBT encode(T value, ClientVersion version); + } } diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentTypes.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentTypes.java index 15cb1bf0af..9b2be98153 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentTypes.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/ComponentTypes.java @@ -56,85 +56,51 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.resources.ResourceLocation; import com.github.retrooper.packetevents.util.Dummy; -import com.github.retrooper.packetevents.util.mappings.MappingHelper; -import com.github.retrooper.packetevents.util.mappings.TypesBuilder; -import com.github.retrooper.packetevents.util.mappings.TypesBuilderData; +import com.github.retrooper.packetevents.util.mappings.VersionedRegistry; import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader; +import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer; import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Function; -public class ComponentTypes { +/** + * Contains all item data component types. + * + * @see EnchantEffectComponentTypes + */ +public final class ComponentTypes { + + private static final VersionedRegistry> REGISTRY = new VersionedRegistry<>( + "data_component_type", "item/item_component_mappings"); - private static final Map> COMPONENT_TYPE_MAP = new HashMap<>(); - private static final Map>> COMPONENT_TYPE_ID_MAP = new HashMap<>(); - private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_component_mappings"); + private ComponentTypes() { + } + @ApiStatus.Internal public static ComponentType define(String key) { return define(key, null, null); } + @ApiStatus.Internal public static ComponentType define(String key, @Nullable Reader reader, @Nullable Writer writer) { - TypesBuilderData data = TYPES_BUILDER.define(key); - ComponentType type = new ComponentType() { - @Override - public T read(PacketWrapper wrapper) { - return reader == null ? null : reader.apply(wrapper); - } - - @Override - public void write(PacketWrapper wrapper, T content) { - if (writer != null) { - writer.accept(wrapper, content); - } - } - - @Override - public ResourceLocation getName() { - return data.getName(); - } - - @Override - public int getId(ClientVersion version) { - return MappingHelper.getId(version, TYPES_BUILDER, data); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof ComponentType) { - return this.getName().equals(((ComponentType) obj).getName()); - } - return false; - } - - @Override - public String toString() { - return "Component[" + this.getName() + "]"; - } - }; + return REGISTRY.define(key, data -> new StaticComponentType<>(data, reader, writer)); + } - MappingHelper.registerMapping(TYPES_BUILDER, COMPONENT_TYPE_MAP, COMPONENT_TYPE_ID_MAP, type); - return type; + public static VersionedRegistry> getRegistry() { + return REGISTRY; } - // with key public static ComponentType getByName(String name) { - return COMPONENT_TYPE_MAP.get(name); + return REGISTRY.getByName(name); } public static ComponentType getById(ClientVersion version, int id) { - int index = TYPES_BUILDER.getDataIndex(version); - Map> idMap = COMPONENT_TYPE_ID_MAP.get((byte) index); - return idMap.get(id); + return REGISTRY.getById(version, id); } - // item component types public static final ComponentType CUSTOM_DATA = define("custom_data", // mojang wraps their "persistent" codec as a stream codec just here, // so packetevents has to handle nbt strings @@ -260,16 +226,10 @@ public static ComponentType getById(ClientVersion version, int id) { * @return Component Types */ public static Collection> values() { - return Collections.unmodifiableCollection(COMPONENT_TYPE_MAP.values()); + return REGISTRY.getEntries(); } static { - TYPES_BUILDER.unloadFileMappings(); + REGISTRY.unloadMappings(); } - - @FunctionalInterface - public interface Reader extends Function, T> {} - - @FunctionalInterface - public interface Writer extends BiConsumer, T> {} } diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/EnchantEffectComponentTypes.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/EnchantEffectComponentTypes.java new file mode 100644 index 0000000000..2c51e27793 --- /dev/null +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/EnchantEffectComponentTypes.java @@ -0,0 +1,120 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.github.retrooper.packetevents.protocol.component; + +import com.github.retrooper.packetevents.protocol.component.ComponentType.Decoder; +import com.github.retrooper.packetevents.protocol.component.ComponentType.Encoder; +import com.github.retrooper.packetevents.protocol.nbt.NBT; +import com.github.retrooper.packetevents.util.mappings.VersionedRegistry; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Contains all enchantment effect component types. + * + * @see ComponentTypes + */ +public final class EnchantEffectComponentTypes { + + private static final VersionedRegistry> REGISTRY = new VersionedRegistry<>( + "enchantment_effect_component_type", "enchantment/effect_component_type"); + + private EnchantEffectComponentTypes() { + } + + @ApiStatus.Internal + public static ComponentType define(String key) { + return define(key, null, null); + } + + @ApiStatus.Internal + public static ComponentType define(String key, @Nullable Decoder reader, @Nullable Encoder writer) { + return REGISTRY.define(key, data -> new StaticComponentType<>(data, reader, writer)); + } + + public static VersionedRegistry> getRegistry() { + return REGISTRY; + } + + // just pass everything through for now + public static ComponentType DAMAGE_PROTECTION = define("damage_protection", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType DAMAGE_IMMUNITY = define("damage_immunity", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType DAMAGE = define("damage", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType SMASH_DAMAGE_PER_FALLEN_BLOCK = define("smash_damage_per_fallen_block", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType KNOCKBACK = define("knockback", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType ARMOR_EFFECTIVENESS = define("armor_effectiveness", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType POST_ATTACK = define("post_attack", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType HIT_BLOCK = define("hit_block", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType ITEM_DAMAGE = define("item_damage", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType ATTRIBUTES = define("attributes", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType EQUIPMENT_DROPS = define("equipment_drops", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType LOCATION_CHANGED = define("location_changed", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType TICK = define("tick", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType AMMO_USE = define("ammo_use", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PROJECTILE_PIERCING = define("projectile_piercing", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PROJECTILE_SPAWNED = define("projectile_spawned", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PROJECTILE_SPREAD = define("projectile_spread", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PROJECTILE_COUNT = define("projectile_count", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType TRIDENT_RETURN_ACCELERATION = define("trident_return_acceleration", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType FISHING_TIME_REDUCTION = define("fishing_time_reduction", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType FISHING_LUCK_BONUS = define("fishing_luck_bonus", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType BLOCK_EXPERIENCE = define("block_experience", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType MOB_EXPERIENCE = define("mob_experience", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType REPAIR_WITH_XP = define("repair_with_xp", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType CROSSBOW_CHARGE_TIME = define("crossbow_charge_time", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType CROSSBOW_CHARGING_SOUNDS = define("crossbow_charging_sounds", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType TRIDENT_SOUND = define("trident_sound", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PREVENT_EQUIPMENT_DROP = define("prevent_equipment_drop", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType PREVENT_ARMOR_CHANGE = define("prevent_armor_change", + (nbt, version) -> nbt, (val, version) -> val); + public static ComponentType TRIDENT_SPIN_ATTACK_STRENGTH = define("trident_spin_attack_strength", + (nbt, version) -> nbt, (val, version) -> val); + + static { + REGISTRY.unloadMappings(); + } +} diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/IComponentMap.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/IComponentMap.java index 6522c9d9d4..d0aafe6caf 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/IComponentMap.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/IComponentMap.java @@ -18,13 +18,44 @@ package com.github.retrooper.packetevents.protocol.component; +import com.github.retrooper.packetevents.protocol.nbt.NBT; +import com.github.retrooper.packetevents.protocol.nbt.NBTCompound; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.util.mappings.IRegistry; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; +import java.util.Map; import java.util.Optional; public interface IComponentMap { + @SuppressWarnings("unchecked") // safe in this case + static StaticComponentMap decode(NBT nbt, ClientVersion version, IRegistry> registry) { + NBTCompound compound = (NBTCompound) nbt; + StaticComponentMap.Builder components = StaticComponentMap.builder(); + for (Map.Entry entry : compound.getTags().entrySet()) { + ComponentType type = registry.getByName(entry.getKey()); + if (type == null) { + throw new IllegalStateException("Unknown component type named " + entry.getKey() + " encountered"); + } + Object value = type.decode(entry.getValue(), version); + components.set((ComponentType) type, value); + } + return components.build(); + } + + @SuppressWarnings("unchecked") // safe in this case + static NBT encode(StaticComponentMap components, ClientVersion version) { + NBTCompound compound = new NBTCompound(); + for (Map.Entry, ?> entry : components.getDelegate().entrySet()) { + String key = entry.getKey().getName().toString(); + NBT value = ((ComponentType) entry.getKey()).encode(entry.getValue(), version); + compound.setTag(key, value); + } + return compound; + } + default Optional getOptional(ComponentType type) { return Optional.ofNullable(this.get(type)); } diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/component/StaticComponentType.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/StaticComponentType.java new file mode 100644 index 0000000000..8561ba8b31 --- /dev/null +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/component/StaticComponentType.java @@ -0,0 +1,94 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.github.retrooper.packetevents.protocol.component; + +import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity; +import com.github.retrooper.packetevents.protocol.nbt.NBT; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.util.mappings.TypesBuilderData; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader; +import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer; +import org.jetbrains.annotations.Nullable; + +public class StaticComponentType extends AbstractMappedEntity implements ComponentType { + + private final @Nullable Reader reader; + private final @Nullable Writer writer; + private final @Nullable Decoder decoder; + private final @Nullable Encoder encoder; + + public StaticComponentType( + @Nullable TypesBuilderData data, + @Nullable Reader reader, + @Nullable Writer writer + ) { + this(data, reader, writer, null, null); + } + + public StaticComponentType( + @Nullable TypesBuilderData data, + @Nullable Decoder decoder, + @Nullable Encoder encoder + ) { + this(data, null, null, decoder, encoder); + } + + public StaticComponentType( + @Nullable TypesBuilderData data, + @Nullable Reader reader, + @Nullable Writer writer, + @Nullable Decoder decoder, + @Nullable Encoder encoder + ) { + super(data); + this.reader = reader; + this.writer = writer; + this.decoder = decoder; + this.encoder = encoder; + } + + @Override + public T read(PacketWrapper wrapper) { + return this.reader != null ? this.reader.apply(wrapper) : null; + } + + @Override + public void write(PacketWrapper wrapper, T content) { + if (this.writer != null) { + this.writer.accept(wrapper, content); + } + } + + @Override + public T decode(NBT nbt, ClientVersion version) { + if (this.decoder != null) { + return this.decoder.decode(nbt, version); + } + throw new UnsupportedOperationException(); + } + + @Override + public NBT encode(T value, ClientVersion version) { + if (this.encoder != null) { + return this.encoder.encode(value, version); + } + throw new UnsupportedOperationException(); + } +} diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/EnchantmentType.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/EnchantmentType.java index d871e7bb7a..c26e564faa 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/EnchantmentType.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/EnchantmentType.java @@ -18,6 +18,7 @@ package com.github.retrooper.packetevents.protocol.item.enchantment.type; +import com.github.retrooper.packetevents.protocol.component.EnchantEffectComponentTypes; import com.github.retrooper.packetevents.protocol.component.IComponentMap; import com.github.retrooper.packetevents.protocol.component.StaticComponentMap; import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDefinition; @@ -42,7 +43,7 @@ public interface EnchantmentType extends MappedEntity, CopyableEntity getExclusiveSet(); - IComponentMap getEffects(); + StaticComponentMap getEffects(); static EnchantmentType decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) { NBTCompound compound = (NBTCompound) nbt; @@ -51,7 +52,10 @@ static EnchantmentType decode(NBT nbt, ClientVersion version, @Nullable TypesBui MappedEntitySet exclusiveSet = Optional.ofNullable(compound.getTagOrNull("exclusive_set")) .map(tag -> MappedEntitySet.decode(tag, version, EnchantmentTypes.getRegistry())) // TODO use user registry .orElseGet(MappedEntitySet::createEmpty); - IComponentMap effects = StaticComponentMap.EMPTY;//IComponentMap.decode(compound.getTagOrThrow("effects"), version, ); FIXME (OPTIONAL) + StaticComponentMap effects = Optional.ofNullable(compound.getTagOrNull("effects")) + .map(tag -> IComponentMap.decode(tag, version, + EnchantEffectComponentTypes.getRegistry())) + .orElse(StaticComponentMap.EMPTY); return new StaticEnchantmentType(data, description, definition, exclusiveSet, effects); } @@ -61,7 +65,9 @@ static NBT encode(EnchantmentType type, ClientVersion version) { if (!type.getExclusiveSet().isEmpty()) { compound.setTag("exclusive_set", MappedEntitySet.encode(type.getExclusiveSet(), version)); } - // compound.setTag("effects", IComponentMap.encode(type.getEffects(), version)); FIXME (OPTIONAL) + if (!type.getEffects().isEmpty()) { + compound.setTag("effects", IComponentMap.encode(type.getEffects(), version)); + } return compound; } } diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/StaticEnchantmentType.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/StaticEnchantmentType.java index bfb563cd4b..3470eb3729 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/StaticEnchantmentType.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/enchantment/type/StaticEnchantmentType.java @@ -19,6 +19,7 @@ package com.github.retrooper.packetevents.protocol.item.enchantment.type; import com.github.retrooper.packetevents.protocol.component.IComponentMap; +import com.github.retrooper.packetevents.protocol.component.StaticComponentMap; import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDefinition; import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity; import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet; @@ -31,13 +32,13 @@ public class StaticEnchantmentType extends AbstractMappedEntity implements Encha private final Component description; private final EnchantmentDefinition definition; private final MappedEntitySet exclusiveSet; - private final IComponentMap effects; + private final StaticComponentMap effects; public StaticEnchantmentType( Component description, EnchantmentDefinition definition, MappedEntitySet exclusiveSet, - IComponentMap effects + StaticComponentMap effects ) { this(null, description, definition, exclusiveSet, effects); } @@ -47,7 +48,7 @@ public StaticEnchantmentType( Component description, EnchantmentDefinition definition, MappedEntitySet exclusiveSet, - IComponentMap effects + StaticComponentMap effects ) { super(data); this.description = description; @@ -78,7 +79,7 @@ public MappedEntitySet getExclusiveSet() { } @Override - public IComponentMap getEffects() { + public StaticComponentMap getEffects() { return this.effects; } } diff --git a/mappings/enchantment/effect_component_type.json b/mappings/enchantment/effect_component_type.json new file mode 100644 index 0000000000..e544128bdb --- /dev/null +++ b/mappings/enchantment/effect_component_type.json @@ -0,0 +1,34 @@ +{ + "V_1_21": [ + "damage_protection", + "damage_immunity", + "damage", + "smash_damage_per_fallen_block", + "knockback", + "armor_effectiveness", + "post_attack", + "hit_block", + "item_damage", + "attributes", + "equipment_drops", + "location_changed", + "tick", + "ammo_use", + "projectile_piercing", + "projectile_spawned", + "projectile_spread", + "projectile_count", + "trident_return_acceleration", + "fishing_time_reduction", + "fishing_luck_bonus", + "block_experience", + "mob_experience", + "repair_with_xp", + "crossbow_charge_time", + "crossbow_charging_sounds", + "trident_sound", + "prevent_equipment_drop", + "prevent_armor_change", + "trident_spin_attack_strength" + ] +}