package com.comphenix.protocol.events; import javax.annotation.Nonnull; import java.lang.reflect.Array; import java.time.Instant; import java.util.*; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.StreamSerializer; import com.comphenix.protocol.wrappers.*; import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.comphenix.protocol.wrappers.nbt.NbtBase; import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.base.Preconditions; import org.apache.commons.lang.Validate; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.World; import org.bukkit.WorldType; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.MerchantRecipe; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; public abstract class AbstractStructure { protected transient Object handle; protected transient StructureModifier structureModifier; protected AbstractStructure() {} protected AbstractStructure(Object handle, StructureModifier modifier) { Validate.notNull(handle, "handle cannot be null"); Validate.notNull(modifier, "modifier cannot be null"); this.handle = handle; this.structureModifier = modifier; } public Object getHandle() { return handle; } public StructureModifier getModifier() { return structureModifier; } /** * Retrieves a read/write structure for every field with the given type. * @param Type * @param primitiveType - the type to find. * @return A modifier for this specific type. */ public StructureModifier getSpecificModifier(Class primitiveType) { return structureModifier.withType(primitiveType); } /** * Retrieves a read/write structure for every byte field. * @return A modifier for every byte field. */ public StructureModifier getBytes() { return structureModifier.withType(byte.class); } /** * Retrieves a read/write structure for every boolean field. * @return A modifier for every boolean field. */ public StructureModifier getBooleans() { return structureModifier.withType(boolean.class); } /** * Retrieves a read/write structure for every short field. * @return A modifier for every short field. */ public StructureModifier getShorts() { return structureModifier.withType(short.class); } /** * Retrieves a read/write structure for every integer field. * @return A modifier for every integer field. */ public StructureModifier getIntegers() { return structureModifier.withType(int.class); } /** * Retrieves a read/write structure for every long field. * @return A modifier for every long field. */ public StructureModifier getLongs() { return structureModifier.withType(long.class); } /** * Retrieves a read/write structure for every float field. * @return A modifier for every float field. */ public StructureModifier getFloat() { return structureModifier.withType(float.class); } /** * Retrieves a read/write structure for every double field. * @return A modifier for every double field. */ public StructureModifier getDoubles() { return structureModifier.withType(double.class); } /** * Retrieves a read/write structure for every String field. * @return A modifier for every String field. */ public StructureModifier getStrings() { return structureModifier.withType(String.class); } /** * Retrieves a read/write structure for every UUID field. * @return A modifier for every UUID field. */ public StructureModifier getUUIDs() { return structureModifier.withType(UUID.class); } /** * Retrieves a read/write structure for every String array field. * @return A modifier for every String array field. */ public StructureModifier getStringArrays() { return structureModifier.withType(String[].class); } /** * Retrieves a read/write structure for every byte array field. * @return A modifier for every byte array field. */ public StructureModifier getByteArrays() { return structureModifier.withType(byte[].class); } /** * Retrieve a serializer for reading and writing ItemStacks stored in a byte array. * @return A instance of the serializer. */ public StreamSerializer getByteArraySerializer() { return new StreamSerializer(); } /** * Retrieves a read/write structure for every int array field. * @return A modifier for every int array field. */ public StructureModifier getIntegerArrays() { return structureModifier.withType(int[].class); } /** * Retrieves a read/write structure for every short array field. * @return A modifier for every short array field. */ public StructureModifier getShortArrays() { return structureModifier.withType(short[].class); } /** * Retrieves a read/write structure for ItemStack. *

* This modifier will automatically marshal between the Bukkit ItemStack and the * internal Minecraft ItemStack. * @return A modifier for ItemStack fields. */ public StructureModifier getItemModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter()); } /** * Retrieves a read/write structure for arrays of ItemStacks. *

* This modifier will automatically marshal between the Bukkit ItemStack and the * internal Minecraft ItemStack. * @return A modifier for ItemStack array fields. */ public StructureModifier getItemArrayModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getItemStackArrayClass(), Converters.ignoreNull(new ItemStackArrayConverter())); } /** * Retrieves a read/write structure for lists of ItemStacks. *

* This modifier will automatically marshal between the Bukkit ItemStack and the * internal Minecraft ItemStack. * @return A modifier for ItemStack list fields. */ public StructureModifier> getItemListModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( List.class, BukkitConverters.getListConverter(BukkitConverters.getItemStackConverter()) ); } /** * Retrieve a read/write structure for maps of statistics. *

* Note that you must write back the changed map to persist it. * @return A modifier for maps of statistics. */ public StructureModifier> getStatisticMaps() { return getMaps( BukkitConverters.getWrappedStatisticConverter(), Converters.passthrough(Integer.class)); } /** * Retrieves a read/write structure for the world type enum. *

* This modifier will automatically marshal between the Bukkit world type and the * internal Minecraft world type. * @return A modifier for world type fields. */ public StructureModifier getWorldTypeModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getWorldTypeClass(), BukkitConverters.getWorldTypeConverter()); } /** * Retrieves a read/write structure for data watchers. * @return A modifier for data watchers. */ public StructureModifier getDataWatcherModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getDataWatcherClass(), BukkitConverters.getDataWatcherConverter()); } /** * Retrieves a read/write structure for entity objects. *

* Note that entities are transmitted by integer ID, and the type may not be enough * to distinguish between entities and other values. Thus, this structure modifier * MAY return null or invalid entities for certain fields. Using the correct index * is essential. * * @param world - the world each entity is currently occupying. * @return A modifier entity types. */ public StructureModifier getEntityModifier(@Nonnull World world) { Preconditions.checkNotNull(world, "world cannot be NULL."); // Convert to and from the Bukkit wrapper return structureModifier.withType( int.class, BukkitConverters.getEntityConverter(world)); } /** * Retrieves a read/write structure for entity objects. *

* Note that entities are transmitted by integer ID, and the type may not be enough * to distinguish between entities and other values. Thus, this structure modifier * MAY return null or invalid entities for certain fields. Using the correct index * is essential. * * @param event - the original packet event. * @return A modifier entity types. */ public StructureModifier getEntityModifier(@Nonnull PacketEvent event) { Preconditions.checkNotNull(event, "event cannot be NULL."); return getEntityModifier(event.getPlayer().getWorld()); } /** * Retrieves a read/write structure for entity types * @return A modifier for an EntityType. */ public StructureModifier getEntityTypeModifier() { return structureModifier.withType( MinecraftReflection.getEntityTypes(), BukkitConverters.getEntityTypeConverter()); } /** * Retrieves a read/write structure for block positions. * @return A modifier for a BlockPosition. */ public StructureModifier getBlockPositionModifier() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getBlockPositionClass(), BlockPosition.getConverter()); } /** * Retrieves a read/write structure for wrapped ChunkCoordIntPairs. * @return A modifier for ChunkCoordIntPair. */ public StructureModifier getChunkCoordIntPairs() { // Allow access to the NBT class in packet 130 return structureModifier.withType( MinecraftReflection.getChunkCoordIntPair(), ChunkCoordIntPair.getConverter()); } /** * Retrieves a read/write structure for NBT classes. * @return A modifier for NBT classes. */ public StructureModifier> getNbtModifier() { // Allow access to the NBT class in packet 130 return structureModifier.withType( MinecraftReflection.getNBTBaseClass(), BukkitConverters.getNbtConverter()); } /** * Retrieves a read/write structure for lists of NBT classes. * @return A modifier for lists of NBT classes. */ public StructureModifier>> getListNbtModifier() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(BukkitConverters.getNbtConverter()) ); } /** * Retrieves a read/write structure for Vectors. * @return A modifier for Vectors. */ public StructureModifier getVectors() { // Automatically marshal between Vec3d and the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()); } /** * Retrieves a read/write structure for collections of attribute snapshots. *

* This modifier will automatically marshal between the visible ProtocolLib WrappedAttribute and the * internal Minecraft AttributeSnapshot. * @return A modifier for AttributeSnapshot collection fields. */ public StructureModifier> getAttributeCollectionModifier() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(BukkitConverters.getWrappedAttributeConverter()) ); } /** * Retrieves a read/write structure for collections of chunk positions. *

* This modifier will automatically marshal between the visible ProtocolLib BlockPosition and the * internal Minecraft BlockPosition. * * @return A modifier for ChunkPosition list fields. */ public StructureModifier> getBlockPositionCollectionModifier() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(BlockPosition.getConverter())); } /** * Retrieves a read/write structure for collections of watchable objects. *

* This modifier will automatically marshal between the visible WrappedWatchableObject and the * internal Minecraft WatchableObject. * @return A modifier for watchable object list fields. */ public StructureModifier> getWatchableCollectionModifier() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(BukkitConverters.getWatchableObjectConverter())); } /** * Retrieves a read/write structure for collections of data values. * @return A modifier for data values. */ public StructureModifier> getDataValueCollectionModifier() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(BukkitConverters.getDataValueConverter())); } /** * Retrieves a read/write structure for block fields. *

* This modifier will automatically marshal between Material and the * internal Minecraft Block. * @return A modifier for GameProfile fields. */ public StructureModifier getBlocks() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getBlockClass(), BukkitConverters.getBlockConverter()); } /** * Retrieves a read/write structure for game profiles in Minecraft 1.7.2. *

* This modifier will automatically marshal between WrappedGameProfile and the * internal Minecraft GameProfile. * @return A modifier for GameProfile fields. */ public StructureModifier getGameProfiles() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter()); } /** * Retrieves a read/write structure for BlockData in Minecraft 1.8. *

* This modifier will automatically marshal between WrappedBlockData and the * internal Minecraft IBlockData. * @return A modifier for BlockData fields. */ public StructureModifier getBlockData() { // Convert to and from our wrapper return structureModifier.withType( MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter() ); } /** * Retrieves a read/write structure for IBlockData arrays in Minecraft 1.16.2+ * @return A modifier for IBlockData array fields */ public StructureModifier getBlockDataArrays() { // TODO we might want to make this a lazy converter and only convert indexes as needed return structureModifier.withType( MinecraftReflection.getArrayClass(MinecraftReflection.getIBlockDataClass()), Converters.array(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter()) ); } /** * Retrieves a read/write structure for MultiBlockChangeInfo arrays in Minecraft 1.8. *

* This modifier will automatically marshal between MultiBlockChangeInfo and the * internal Minecraft MultiBlockChangeInfo. * @return A modifier for BlockData fields. */ public StructureModifier getMultiBlockChangeInfoArrays() { ChunkCoordIntPair chunk = getChunkCoordIntPairs().read(0); // Convert to and from our wrapper return structureModifier.withType( MinecraftReflection.getMultiBlockChangeInfoArrayClass(), Converters.array(MinecraftReflection.getMultiBlockChangeInfoClass(), MultiBlockChangeInfo.getConverter(chunk)) ); } /** * Retrieves a read/write structure for chat components in Minecraft 1.7.2. *

* This modifier will automatically marshal between WrappedChatComponent and the * internal Minecraft IChatBaseComponent. * @return A modifier for ChatComponent fields. */ public StructureModifier getChatComponents() { // Convert to and from the Bukkit wrapper return structureModifier.withType( MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter()); } /** * Retrieves a read/write structure for arrays of chat components. *

* This modifier will automatically marshal between WrappedChatComponent and the * internal Minecraft IChatBaseComponent (1.9.2 and below) or the internal * NBTCompound (1.9.4 and up). *

* Note that in 1.9.4 and up this modifier only works properly with sign * tile entities. * @return A modifier for ChatComponent array fields. */ public StructureModifier getChatComponentArrays() { // Convert to and from the Bukkit wrapper return structureModifier.withType( ComponentArrayConverter.getGenericType(), Converters.ignoreNull(new ComponentArrayConverter())); } /** * Retrieve a read/write structure for the ServerPing fields in the following packet:
*

    *
  • {@link PacketType.Status.Server#SERVER_INFO} *
* @return A modifier for ServerPing fields. */ public StructureModifier getServerPings() { // Convert to and from the wrapper return structureModifier.withType( MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter()); } /** * Retrieve a read/write structure for the PlayerInfoData list fields in the following packet:
*
    *
  • {@link PacketType.Play.Server#PLAYER_INFO} *
* @return A modifier for PlayerInfoData list fields. */ public StructureModifier> getPlayerInfoDataLists() { // Convert to and from the ProtocolLib wrapper return structureModifier.withType( Collection.class, BukkitConverters.getListConverter(PlayerInfoData.getConverter())); } /** * Retrieve a read/write structure for the Protocol enum in 1.7.2. * @return A modifier for Protocol enum fields. */ public StructureModifier getProtocols() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getProtocolClass(), EnumWrappers.getProtocolConverter()); } /** * Retrieve a read/write structure for the ClientCommand enum in 1.7.2. * @return A modifier for ClientCommand enum fields. */ public StructureModifier getClientCommands() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getClientCommandClass(), EnumWrappers.getClientCommandConverter()); } /** * Retrieve a read/write structure for the ChatVisibility enum in 1.7.2. * @return A modifier for ChatVisibility enum fields. */ public StructureModifier getChatVisibilities() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getChatVisibilityClass(), EnumWrappers.getChatVisibilityConverter()); } /** * Retrieve a read/write structure for the Difficulty enum in 1.7.2. * @return A modifier for Difficulty enum fields. */ public StructureModifier getDifficulties() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getDifficultyClass(), EnumWrappers.getDifficultyConverter()); } /** * Retrieve a read/write structure for the EntityUse enum in 1.7.2. * @return A modifier for EntityUse enum fields. */ public StructureModifier getEntityUseActions() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getEntityUseActionClass(), EnumWrappers.getEntityUseActionConverter()); } /** * Retrieves a read/write structure for the EntityUseAction class in the UseEntity packet sent by the client for * 1.17 and above. * @return A modifier for EntityUseAction class fields. */ public StructureModifier getEnumEntityUseActions() { return structureModifier.withType( MinecraftReflection.getEnumEntityUseActionClass(), WrappedEnumEntityUseAction.CONVERTER); } /** * Retrieve a read/write structure for the NativeGameMode enum in 1.7.2. * @return A modifier for NativeGameMode enum fields. */ public StructureModifier getGameModes() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter()); } /** * Retrieve a read/write structure for the ResourcePackStatus enum in 1.8. * @return A modifier for ResourcePackStatus enum fields. */ public StructureModifier getResourcePackStatus() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getResourcePackStatusClass(), EnumWrappers.getResourcePackStatusConverter()); } /** * Retrieve a read/write structure for the PlayerInfo enum in 1.8. * @return A modifier for PlayerInfoAction enum fields. */ public StructureModifier getPlayerInfoAction() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getPlayerInfoActionClass(), EnumWrappers.getPlayerInfoActionConverter()); } /** * Retrieve a read/write structure for an EnumSet of PlayerInfos. * @return A modifier for an EnumSet of PlayerInfo fields. */ public StructureModifier> getPlayerInfoActions() { // Convert to and from the wrapper return structureModifier.withType( EnumSet.class, Converters.collection( EnumWrappers.getPlayerInfoActionConverter(), generic -> EnumSet.noneOf(EnumWrappers.PlayerInfoAction.class), specific -> EnumWrappers.createEmptyEnumSet(EnumWrappers.getPlayerInfoActionClass()))); } /** * Retrieve a read/write structure for the TitleAction enum in 1.8. * @return A modifier for TitleAction enum fields. */ public StructureModifier getTitleActions() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getTitleActionClass(), EnumWrappers.getTitleActionConverter()); } /** * Retrieve a read/write structure for the WorldBorderAction enum in 1.8. * @return A modifier for WorldBorderAction enum fields. */ public StructureModifier getWorldBorderActions() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getWorldBorderActionClass(), EnumWrappers.getWorldBorderActionConverter()); } /** * Retrieve a read/write structure for the CombatEventType enum in 1.8. * @return A modifier for CombatEventType enum fields. */ public StructureModifier getCombatEvents() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getCombatEventTypeClass(), EnumWrappers.getCombatEventTypeConverter()); } /** * Retrieve a read/write structure for the PlayerDigType enum in 1.8. * @return A modifier for PlayerDigType enum fields. */ public StructureModifier getPlayerDigTypes() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getPlayerDigTypeClass(), EnumWrappers.getPlayerDiggingActionConverter()); } /** * Retrieve a read/write structure for the PlayerAction enum in 1.8. * @return A modifier for PlayerAction enum fields. */ public StructureModifier getPlayerActions() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getPlayerActionClass(), EnumWrappers.getEntityActionConverter()); } /** * Retrieve a read/write structure for the ScoreboardAction enum in 1.8. * @return A modifier for ScoreboardAction enum fields. */ public StructureModifier getScoreboardActions() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getScoreboardActionClass(), EnumWrappers.getUpdateScoreActionConverter()); } /** * Retrieve a read/write structure for the Particle enum in 1.8-1.12. * NOTE: This will produce undesirable results in 1.13 * @return A modifier for Particle enum fields. */ public StructureModifier getParticles() { // Convert to and from the wrapper return structureModifier.withType( EnumWrappers.getParticleClass(), EnumWrappers.getParticleConverter()); } /** * Retrieve a read/write structure for ParticleParams in 1.13 * @return A modifier for ParticleParam fields. */ public StructureModifier getNewParticles() { return structureModifier.withType( MinecraftReflection.getParticleParam(), BukkitConverters.getParticleConverter() ); } /** * Retrieve a read/write structure for the MobEffectList class in 1.9. * @return A modifier for MobEffectList fields. */ public StructureModifier getEffectTypes() { // Convert to and from Bukkit return structureModifier.withType( MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter()); } /** * Retrieve a read/write structure for the SoundCategory enum in 1.9. * @return A modifier for SoundCategory enum fields. */ public StructureModifier getSoundCategories() { // Convert to and from the enums return structureModifier.withType( EnumWrappers.getSoundCategoryClass(), EnumWrappers.getSoundCategoryConverter()); } /** * Retrieve a read/write structure for a Holder<T> in 1.19.3. * @param genericType NMS type of T * @param converter Converter from genericType to T * @return A modifier for Holder fields * @param Bukkit type */ public StructureModifier getHolders(Class genericType, EquivalentConverter converter) { return structureModifier.withParamType( MinecraftReflection.getHolderClass(), Converters.holder(converter, WrappedRegistry.getRegistry(genericType)), genericType ); } /** * Retrieve a read/write structure for the SoundEffect enum in 1.9. * @return A modifier for SoundEffect enum fields. */ public StructureModifier getSoundEffects() { if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { return getHolders(MinecraftReflection.getSoundEffectClass(), BukkitConverters.getSoundConverter()); } // Convert to and from Bukkit return structureModifier.withType( MinecraftReflection.getSoundEffectClass(), BukkitConverters.getSoundConverter()); } /** * Retrieve a read/write structure for the ItemSlot enum in 1.9. * @return A modifier for ItemSlot enum fields. */ public StructureModifier getItemSlots() { return structureModifier.withType( EnumWrappers.getItemSlotClass(), EnumWrappers.getItemSlotConverter()); } /** * Retrieve a read/write structure for the Hand enum in 1.9. * @return A modifier for Hand enum fields. */ public StructureModifier getHands() { return structureModifier.withType( EnumWrappers.getHandClass(), EnumWrappers.getHandConverter()); } /** * Retrieve a read/write structure for the Direction enum in 1.10. * @return A modifier for Direction enum fields. */ public StructureModifier getDirections() { return structureModifier.withType( EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter()); } /** * Retrieve a read/write structure for the ChatType enum in 1.12. * @return A modifier for ChatType enum fields. */ public StructureModifier getChatTypes() { return structureModifier.withType( EnumWrappers.getChatTypeClass(), EnumWrappers.getChatTypeConverter()); } /** * Retrieve a read/write structure for the MinecraftKey class. * @return A modifier for MinecraftKey fields. */ public StructureModifier getMinecraftKeys() { return structureModifier.withType( MinecraftReflection.getMinecraftKeyClass(), MinecraftKey.getConverter()); } /** * Retrieve a read/write structure for dimension IDs in 1.13.1+ * @return A modifier for dimension IDs */ @Deprecated public StructureModifier getDimensions() { if (MinecraftVersion.NETHER_UPDATE.atOrAbove() && !MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) { return structureModifier.withParamType( MinecraftReflection.getResourceKey(), BukkitConverters.getDimensionIDConverter(), MinecraftReflection.getDimensionManager() ); } else { return structureModifier.withType( MinecraftReflection.getDimensionManager(), BukkitConverters.getDimensionIDConverter() ); } } public StructureModifier getDimensionTypes() { return structureModifier.withType( MinecraftReflection.getDimensionManager(), BukkitConverters.getDimensionConverter() ); } /** * Retrieve a read/write structure for the MerchantRecipeList class. * @return A modifier for MerchantRecipeList fields. */ public StructureModifier> getMerchantRecipeLists() { return structureModifier.withType( MinecraftReflection.getMerchantRecipeList(), BukkitConverters.getMerchantRecipeListConverter() ); } /** * Retrieve a read/write structure for ItemSlot/ItemStack pair lists in 1.16+ * @return The Structure Modifier */ public StructureModifier>> getSlotStackPairLists() { return getLists(BukkitConverters.getPairConverter( EnumWrappers.getItemSlotConverter(), BukkitConverters.getItemStackConverter() )); } /** * Retrieve a read/write structure for MovingObjectPositionBlock in 1.16+ * @return The Structure Modifier */ public StructureModifier getMovingBlockPositions() { return structureModifier.withType( MovingObjectPositionBlock.getNmsClass(), MovingObjectPositionBlock.getConverter() ); } /** * Retrieve a read/write structure for World ResourceKeys in 1.16+ * @return The Structure Modifier */ public StructureModifier getWorldKeys() { return structureModifier.withParamType( MinecraftReflection.getResourceKey(), BukkitConverters.getWorldKeyConverter(), MinecraftReflection.getNmsWorldClass() ); } /** * Retrieve a read/write structure for SectionPositions in 1.16.2+ * @return The Structure Modifier */ public StructureModifier getSectionPositions() { return structureModifier.withType( MinecraftReflection.getSectionPosition(), BukkitConverters.getSectionPositionConverter() ); } /** * Retrieve a read/write structure for Game State IDs in 1.16+ * @return The Structure Modifier */ public StructureModifier getGameStateIDs() { return structureModifier.withType( MinecraftReflection.getGameStateClass(), BukkitConverters.getGameStateConverter() ); } public StructureModifier> getIntLists() { return structureModifier.withType( List.class, BukkitConverters.getListConverter( MinecraftReflection.getIntArrayListClass(), Converters.passthrough(int.class) ) ); } public StructureModifier> getUUIDLists() { return structureModifier.withType( List.class, BukkitConverters.getListConverter(Converters.passthrough(UUID.class))); } /** * Retrieve a read/write structure for Instants in (mostly for use in 1.19+) * @return The Structure Modifier */ public StructureModifier getInstants() { return structureModifier.withType(Instant.class); } /** * Retrieve a read/write structure for profile public keys in 1.19 * @return The Structure Modifier */ public StructureModifier getProfilePublicKeys() { return structureModifier.withType( MinecraftReflection.getProfilePublicKeyClass(), BukkitConverters.getWrappedProfilePublicKeyConverter()); } /** * Retrieve a read/write structure for profile public key data in 1.19 * @return The Structure Modifier */ public StructureModifier getProfilePublicKeyData() { return structureModifier.withType( MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()); } /** * Retrieves read/write structure for remote chat session data in 1.19.3 * @return The Structure Modifier */ public StructureModifier getRemoteChatSessionData() { return structureModifier.withType( MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter() ); } /** * Retrieve a read/write structure for LevelChunkPacketData in 1.18+ * * @return The Structure Modifier */ public StructureModifier getLevelChunkData() { return structureModifier.withType(MinecraftReflection.getLevelChunkPacketDataClass(), BukkitConverters.getWrappedChunkDataConverter()); } /** * Retrieve a read/write structure for LightUpdatePacketData in 1.18+ * * @return The Structure Modifier */ public StructureModifier getLightUpdateData() { return structureModifier.withType(MinecraftReflection.getLightUpdatePacketDataClass(), BukkitConverters.getWrappedLightDataConverter()); } /** * @return read/write structure for login encryption packets */ public StructureModifier> getLoginSignatures() { return getEithers(Converters.passthrough(byte[].class), BukkitConverters.getWrappedSignatureConverter()); } /** * @return read/writer structure direct access to salted signature data like chat messages */ public StructureModifier getSignatures() { return structureModifier.withType( MinecraftReflection.getSaltedSignatureClass(), BukkitConverters.getWrappedSignatureConverter() ); } /** * @return read/writer structure direct access to unsalted signature data for example in chat message (since 1.19.3) */ public StructureModifier getMessageSignatures() { return structureModifier.withType( MinecraftReflection.getMessageSignatureClass(), BukkitConverters.getWrappedMessageSignatureConverter() ); } /** * @param leftConverter converter for left values * @param rightConverter converter for right values * @return ProtocolLib's read/write structure for Mojang either structures * @param left data type after converting from NMS * @param right data type after converting from NMS */ public StructureModifier> getEithers(EquivalentConverter leftConverter, EquivalentConverter rightConverter) { return structureModifier.withType( com.mojang.datafixers.util.Either.class, BukkitConverters.getEitherConverter( leftConverter, rightConverter ) ); } /** * Retrieve a read/write structure for the Map class. * @param keyConverter Converter for map keys * @param valConverter Converter for map values * @param Key param * @param Value param * @return A modifier for Map fields. * * @see BukkitConverters * @see EquivalentConverter */ public StructureModifier> getMaps(EquivalentConverter keyConverter, EquivalentConverter valConverter) { return structureModifier.withType( Map.class, BukkitConverters.getMapConverter(keyConverter, valConverter)); } /** * Retrieve a read/write structure for the Set class. * @param converter Converter for elements * @param Element param * @return A modifier for Set fields * * @see BukkitConverters * @see EquivalentConverter */ public StructureModifier> getSets(EquivalentConverter converter) { return structureModifier.withType( Set.class, BukkitConverters.getSetConverter(converter)); } /** * Retrieve a read/write structure for the List class. * @param converter Converter for elements * @param Element param * @return A modifier for List fields */ public StructureModifier> getLists(EquivalentConverter converter) { return structureModifier.withType( List.class, BukkitConverters.getListConverter(converter)); } /** * Retrieve a read/write structure for an enum. This allows for the use of * user-created enums that may not exist in ProtocolLib. The specific (user * created) enum constants must match up perfectly with their generic (NMS) * counterparts. * * @param enumClass The specific Enum class * @param nmsClass The generic Enum class * @return The modifier */ public > StructureModifier getEnumModifier(Class enumClass, Class nmsClass) { return structureModifier.withType( nmsClass, new EnumWrappers.EnumConverter<>(nmsClass, enumClass)); } /** * Retrieve a read/write structure for an enum. This method is for convenience, * see {@link #getEnumModifier(Class, Class)} for more information. * * @param enumClass The specific Enum class * @param index Index of the generic Enum * @return The modifier * @see #getEnumModifier(Class, Class) */ public > StructureModifier getEnumModifier(Class enumClass, int index) { return getEnumModifier( enumClass, structureModifier.getField(index).getType()); } /** * Retrieve a read/write structure for an optional, passing * the value of the optional field through the given converter * if present. * * @param converter Converter for internal element of optional, if present * @param The inner type of the optional * @return The modifier */ public StructureModifier> getOptionals(EquivalentConverter converter) { return structureModifier.withType(Optional.class, Converters.optional(converter)); } public StructureModifier> getPacketBundles() { return structureModifier.withType(Iterable.class, Converters.iterable( BukkitConverters.getPacketContainerConverter(), ArrayList::new, ArrayList::new )); } /** * Represents an equivalent converter for ItemStack arrays. * @author Kristian */ private static class ItemStackArrayConverter implements EquivalentConverter { final EquivalentConverter stackConverter = BukkitConverters.getItemStackConverter(); @Override public Object getGeneric(ItemStack[] specific) { Class nmsStack = MinecraftReflection.getItemStackClass(); Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length); // Unwrap every item for (int i = 0; i < result.length; i++) { result[i] = stackConverter.getGeneric(specific[i]); } return result; } @Override public ItemStack[] getSpecific(Object generic) { Object[] input = (Object[]) generic; ItemStack[] result = new ItemStack[input.length]; // Add the wrapper for (int i = 0; i < result.length; i++) { result[i] = stackConverter.getSpecific(input[i]); } return result; } @Override public Class getSpecificType() { return ItemStack[].class; } } /** * Represents an equivalent converter for ChatComponent arrays. * @author dmulloy2 */ private static class LegacyComponentConverter implements EquivalentConverter { final EquivalentConverter componentConverter = BukkitConverters.getWrappedChatComponentConverter(); @Override public Object getGeneric(WrappedChatComponent[] specific) { Class nmsComponent = MinecraftReflection.getIChatBaseComponentClass(); Object[] result = (Object[]) Array.newInstance(nmsComponent, specific.length); // Unwrap every item for (int i = 0; i < result.length; i++) { result[i] = componentConverter.getGeneric(specific[i]); } return result; } @Override public WrappedChatComponent[] getSpecific(Object generic) { Object[] input = (Object[]) generic; WrappedChatComponent[] result = new WrappedChatComponent[input.length]; // Add the wrapper for (int i = 0; i < result.length; i++) { result[i] = componentConverter.getSpecific(input[i]); } return result; } @Override public Class getSpecificType() { return WrappedChatComponent[].class; } } /** * Converts from NBT to WrappedChatComponent arrays * @author dmulloy2 */ private static class NBTComponentConverter implements EquivalentConverter { private EquivalentConverter> nbtConverter = BukkitConverters.getNbtConverter(); private final int lines = 4; @Override public WrappedChatComponent[] getSpecific(Object generic) { NbtBase nbtBase = nbtConverter.getSpecific(generic); NbtCompound compound = (NbtCompound) nbtBase; WrappedChatComponent[] components = new WrappedChatComponent[lines]; for (int i = 0; i < lines; i++) { if (compound.containsKey("Text" + (i + 1))) { components[i] = WrappedChatComponent.fromJson(compound.getString("Text" + (i + 1))); } else { components[i] = WrappedChatComponent.fromText(""); } } return components; } @Override public Object getGeneric(WrappedChatComponent[] specific) { NbtCompound compound = NbtFactory.ofCompound(""); for (int i = 0; i < lines; i++) { WrappedChatComponent component; if (i < specific.length && specific[i] != null) { component = specific[i]; } else { component = WrappedChatComponent.fromText(""); } compound.put("Text" + (i + 1), component.getJson()); } return nbtConverter.getGeneric(compound); } @Override public Class getSpecificType() { return WrappedChatComponent[].class; } } /** * A delegated converter that supports NBT to Component Array and regular Component Array * @author dmulloy2 */ private static class ComponentArrayConverter implements EquivalentConverter { private static final EquivalentConverter DELEGATE; static { if (MinecraftReflection.signUpdateExists()) { DELEGATE = new LegacyComponentConverter(); } else { DELEGATE = new NBTComponentConverter(); } } @Override public WrappedChatComponent[] getSpecific(Object generic) { return DELEGATE.getSpecific(generic); } @Override public Object getGeneric(WrappedChatComponent[] specific) { return DELEGATE.getGeneric(specific); } @Override public Class getSpecificType() { return DELEGATE.getSpecificType(); } public static Class getGenericType() { if (DELEGATE instanceof NBTComponentConverter) { return MinecraftReflection.getNBTCompoundClass(); } else { return MinecraftReflection.getIChatBaseComponentArrayClass(); } } } }