From ceefdf7bfbddb76ce5e8bb6b744e4343395ad1a7 Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Fri, 28 Nov 2025 23:39:57 +0100 Subject: [PATCH 1/6] wip: reworked DataComponentAdapters to be more generic and usable in multiple places --- .../datacomponent/DataComponentAdapters.java | 250 ------------------ .../datacomponent/PaperDataComponentType.java | 212 ++++++++++++++- .../PaperDataComponentTypeCollector.java | 27 ++ .../typed/AbstractTypedDataCollector.java | 75 ++++++ .../data/typed/PaperTypedDataAdapter.java} | 15 +- .../data/typed/PaperTypedDataAdapters.java | 49 ++++ .../data/typed/PaperTypedDataCollector.java | 26 ++ .../registry/data/typed/package-info.java | 4 + 8 files changed, 390 insertions(+), 268 deletions(-) delete mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java create mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentTypeCollector.java create mode 100644 paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java rename paper-server/src/main/java/io/papermc/paper/{datacomponent/DataComponentAdapter.java => registry/data/typed/PaperTypedDataAdapter.java} (69%) create mode 100644 paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapters.java create mode 100644 paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataCollector.java create mode 100644 paper-server/src/main/java/io/papermc/paper/registry/data/typed/package-info.java diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java deleted file mode 100644 index f6783d5f9279..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java +++ /dev/null @@ -1,250 +0,0 @@ -package io.papermc.paper.datacomponent; - -import io.papermc.paper.adventure.PaperAdventure; -import io.papermc.paper.datacomponent.item.PaperAttackRange; -import io.papermc.paper.datacomponent.item.PaperBannerPatternLayers; -import io.papermc.paper.datacomponent.item.PaperBlockItemDataProperties; -import io.papermc.paper.datacomponent.item.PaperBlocksAttacks; -import io.papermc.paper.datacomponent.item.PaperBundleContents; -import io.papermc.paper.datacomponent.item.PaperChargedProjectiles; -import io.papermc.paper.datacomponent.item.PaperConsumable; -import io.papermc.paper.datacomponent.item.PaperCustomModelData; -import io.papermc.paper.datacomponent.item.PaperDamageResistant; -import io.papermc.paper.datacomponent.item.PaperDeathProtection; -import io.papermc.paper.datacomponent.item.PaperDyedItemColor; -import io.papermc.paper.datacomponent.item.PaperEnchantable; -import io.papermc.paper.datacomponent.item.PaperEquippable; -import io.papermc.paper.datacomponent.item.PaperFireworks; -import io.papermc.paper.datacomponent.item.PaperFoodProperties; -import io.papermc.paper.datacomponent.item.PaperItemAdventurePredicate; -import io.papermc.paper.datacomponent.item.PaperItemArmorTrim; -import io.papermc.paper.datacomponent.item.PaperItemAttributeModifiers; -import io.papermc.paper.datacomponent.item.PaperItemContainerContents; -import io.papermc.paper.datacomponent.item.PaperItemEnchantments; -import io.papermc.paper.datacomponent.item.PaperItemLore; -import io.papermc.paper.datacomponent.item.PaperItemTool; -import io.papermc.paper.datacomponent.item.PaperJukeboxPlayable; -import io.papermc.paper.datacomponent.item.PaperKineticWeapon; -import io.papermc.paper.datacomponent.item.PaperLodestoneTracker; -import io.papermc.paper.datacomponent.item.PaperMapDecorations; -import io.papermc.paper.datacomponent.item.PaperMapId; -import io.papermc.paper.datacomponent.item.PaperMapItemColor; -import io.papermc.paper.datacomponent.item.PaperOminousBottleAmplifier; -import io.papermc.paper.datacomponent.item.PaperPiercingWeapon; -import io.papermc.paper.datacomponent.item.PaperPotDecorations; -import io.papermc.paper.datacomponent.item.PaperPotionContents; -import io.papermc.paper.datacomponent.item.PaperRepairable; -import io.papermc.paper.datacomponent.item.PaperResolvableProfile; -import io.papermc.paper.datacomponent.item.PaperSeededContainerLoot; -import io.papermc.paper.datacomponent.item.PaperSuspiciousStewEffects; -import io.papermc.paper.datacomponent.item.PaperSwingAnimation; -import io.papermc.paper.datacomponent.item.PaperTooltipDisplay; -import io.papermc.paper.datacomponent.item.PaperUseCooldown; -import io.papermc.paper.datacomponent.item.PaperUseEffects; -import io.papermc.paper.datacomponent.item.PaperUseRemainder; -import io.papermc.paper.datacomponent.item.PaperWeapon; -import io.papermc.paper.datacomponent.item.PaperWritableBookContent; -import io.papermc.paper.datacomponent.item.PaperWrittenBookContent; -import io.papermc.paper.registry.PaperRegistries; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.util.Unit; -import net.minecraft.world.item.EitherHolder; -import net.minecraft.world.item.Rarity; -import net.minecraft.world.item.component.InstrumentComponent; -import net.minecraft.world.item.component.MapPostProcessing; -import net.minecraft.world.item.component.ProvidesTrimMaterial; -import org.bukkit.DyeColor; -import org.bukkit.craftbukkit.CraftArt; -import org.bukkit.craftbukkit.CraftMusicInstrument; -import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.damage.CraftDamageType; -import org.bukkit.craftbukkit.entity.CraftCat; -import org.bukkit.craftbukkit.entity.CraftChicken; -import org.bukkit.craftbukkit.entity.CraftCow; -import org.bukkit.craftbukkit.entity.CraftFrog; -import org.bukkit.craftbukkit.entity.CraftPig; -import org.bukkit.craftbukkit.entity.CraftVillager; -import org.bukkit.craftbukkit.entity.CraftWolf; -import org.bukkit.craftbukkit.entity.CraftZombieNautilus; -import org.bukkit.craftbukkit.inventory.CraftMetaFirework; -import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; -import org.bukkit.craftbukkit.util.Handleable; -import org.bukkit.entity.Axolotl; -import org.bukkit.entity.Horse; -import org.bukkit.entity.Llama; -import org.bukkit.entity.MushroomCow; -import org.bukkit.entity.Parrot; -import org.bukkit.entity.Rabbit; -import org.bukkit.entity.Salmon; -import org.bukkit.entity.TropicalFish; -import org.bukkit.inventory.ItemRarity; - -import static io.papermc.paper.util.MCUtil.transformUnmodifiable; - -public final class DataComponentAdapters { - - static final Function UNIT_TO_API_CONVERTER = $ -> { - throw new UnsupportedOperationException("Cannot convert the Unit type to an API value"); - }; - - static final Function UNIMPLEMENTED_TO_API_CONVERTER = $ -> { - throw new UnsupportedOperationException("Cannot convert the an unimplemented type to an API value"); - }; - - static final Map>, DataComponentAdapter> ADAPTERS = new HashMap<>(); - - public static void bootstrap() { - registerIdentity(DataComponents.MAX_STACK_SIZE); - registerIdentity(DataComponents.MAX_DAMAGE); - registerIdentity(DataComponents.DAMAGE); - registerUntyped(DataComponents.UNBREAKABLE); - register(DataComponents.USE_EFFECTS, PaperUseEffects::new); - registerIdentity(DataComponents.POTION_DURATION_SCALE); - register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); - registerIdentity(DataComponents.MINIMUM_ATTACK_CHARGE); - register(DataComponents.DAMAGE_TYPE, nms -> CraftDamageType.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftDamageType.bukkitToMinecraftHolder(api))); - register(DataComponents.ITEM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); - register(DataComponents.ITEM_MODEL, PaperAdventure::asAdventure, PaperAdventure::asVanilla); - register(DataComponents.LORE, PaperItemLore::new); - register(DataComponents.RARITY, nms -> ItemRarity.valueOf(nms.name()), api -> Rarity.valueOf(api.name())); - register(DataComponents.ENCHANTMENTS, PaperItemEnchantments::new); - register(DataComponents.CAN_PLACE_ON, PaperItemAdventurePredicate::new); - register(DataComponents.CAN_BREAK, PaperItemAdventurePredicate::new); - register(DataComponents.ATTRIBUTE_MODIFIERS, PaperItemAttributeModifiers::new); - register(DataComponents.CUSTOM_MODEL_DATA, PaperCustomModelData::new); - registerIdentity(DataComponents.REPAIR_COST); - // registerUntyped(DataComponents.CREATIVE_SLOT_LOCK); - registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE); - registerUntyped(DataComponents.INTANGIBLE_PROJECTILE); - register(DataComponents.FOOD, PaperFoodProperties::new); - register(DataComponents.CONSUMABLE, PaperConsumable::new); - register(DataComponents.USE_REMAINDER, PaperUseRemainder::new); - register(DataComponents.USE_COOLDOWN, PaperUseCooldown::new); - register(DataComponents.DAMAGE_RESISTANT, PaperDamageResistant::new); - register(DataComponents.TOOL, PaperItemTool::new); - register(DataComponents.ENCHANTABLE, PaperEnchantable::new); - register(DataComponents.EQUIPPABLE, PaperEquippable::new); - register(DataComponents.REPAIRABLE, PaperRepairable::new); - registerUntyped(DataComponents.GLIDER); - register(DataComponents.TOOLTIP_STYLE, PaperAdventure::asAdventure, PaperAdventure::asVanilla); - register(DataComponents.DEATH_PROTECTION, PaperDeathProtection::new); - register(DataComponents.STORED_ENCHANTMENTS, PaperItemEnchantments::new); - register(DataComponents.DYED_COLOR, PaperDyedItemColor::new); - register(DataComponents.MAP_COLOR, PaperMapItemColor::new); - register(DataComponents.MAP_ID, PaperMapId::new); - register(DataComponents.MAP_DECORATIONS, PaperMapDecorations::new); - register(DataComponents.MAP_POST_PROCESSING, nms -> io.papermc.paper.item.MapPostProcessing.valueOf(nms.name()), api -> MapPostProcessing.valueOf(api.name())); - register(DataComponents.CHARGED_PROJECTILES, PaperChargedProjectiles::new); - register(DataComponents.BUNDLE_CONTENTS, PaperBundleContents::new); - register(DataComponents.POTION_CONTENTS, PaperPotionContents::new); - register(DataComponents.SUSPICIOUS_STEW_EFFECTS, PaperSuspiciousStewEffects::new); - register(DataComponents.WRITTEN_BOOK_CONTENT, PaperWrittenBookContent::new); - register(DataComponents.WRITABLE_BOOK_CONTENT, PaperWritableBookContent::new); - register(DataComponents.TRIM, PaperItemArmorTrim::new); - // debug stick state - // entity data - // bucket entity data - // block entity data - register(DataComponents.INSTRUMENT, nms -> CraftMusicInstrument.minecraftHolderToBukkit(nms.instrument().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new InstrumentComponent(CraftMusicInstrument.bukkitToMinecraftHolder(api))); - register(DataComponents.PROVIDES_TRIM_MATERIAL, nms -> CraftTrimMaterial.minecraftHolderToBukkit(nms.material().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new ProvidesTrimMaterial(CraftTrimMaterial.bukkitToMinecraftHolder(api))); - register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new); - register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new); - register(DataComponents.PROVIDES_BANNER_PATTERNS, PaperRegistries::fromNms, PaperRegistries::toNms); - register( - DataComponents.RECIPES, - nms -> transformUnmodifiable(nms, PaperAdventure::asAdventureKey), - api -> transformUnmodifiable(api, key -> PaperAdventure.asVanilla(Registries.RECIPE, key)) - ); - register(DataComponents.LODESTONE_TRACKER, PaperLodestoneTracker::new); - register(DataComponents.FIREWORK_EXPLOSION, CraftMetaFirework::getEffect, CraftMetaFirework::getExplosion); - register(DataComponents.FIREWORKS, PaperFireworks::new); - register(DataComponents.PROFILE, PaperResolvableProfile::new); - register(DataComponents.NOTE_BLOCK_SOUND, PaperAdventure::asAdventure, PaperAdventure::asVanilla); - register(DataComponents.BANNER_PATTERNS, PaperBannerPatternLayers::new); - register(DataComponents.BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.POT_DECORATIONS, PaperPotDecorations::new); - register(DataComponents.CONTAINER, PaperItemContainerContents::new); - register(DataComponents.BLOCK_STATE, PaperBlockItemDataProperties::new); - // bees - // register(DataComponents.LOCK, PaperLockCode::new); - register(DataComponents.CONTAINER_LOOT, PaperSeededContainerLoot::new); - register(DataComponents.BREAK_SOUND, nms -> PaperAdventure.asAdventure(nms.value().location()), PaperAdventure::resolveSound); - register(DataComponents.TOOLTIP_DISPLAY, PaperTooltipDisplay::new); - register(DataComponents.WEAPON, PaperWeapon::new); - register(DataComponents.BLOCKS_ATTACKS, PaperBlocksAttacks::new); - register(DataComponents.PIERCING_WEAPON, PaperPiercingWeapon::new); - register(DataComponents.KINETIC_WEAPON, PaperKineticWeapon::new); - register(DataComponents.ATTACK_RANGE, PaperAttackRange::new); - register(DataComponents.SWING_ANIMATION, PaperSwingAnimation::new); - register(DataComponents.VILLAGER_VARIANT, CraftVillager.CraftType::minecraftHolderToBukkit, CraftVillager.CraftType::bukkitToMinecraftHolder); - register(DataComponents.WOLF_VARIANT, CraftWolf.CraftVariant::minecraftHolderToBukkit, CraftWolf.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.WOLF_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.WOLF_SOUND_VARIANT, CraftWolf.CraftSoundVariant::minecraftHolderToBukkit, CraftWolf.CraftSoundVariant::bukkitToMinecraftHolder); - register(DataComponents.FOX_VARIANT, nms -> org.bukkit.entity.Fox.Type.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fox.Fox.Variant.byId(api.ordinal())); - register(DataComponents.SALMON_SIZE, nms -> Salmon.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fish.Salmon.Variant.values()[api.ordinal()]); - register(DataComponents.PARROT_VARIANT, nms -> Parrot.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.parrot.Parrot.Variant.byId(api.ordinal())); - register(DataComponents.TROPICAL_FISH_PATTERN, nms -> TropicalFish.Pattern.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fish.TropicalFish.Pattern.values()[api.ordinal()]); - register(DataComponents.TROPICAL_FISH_BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.TROPICAL_FISH_PATTERN_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.MOOSHROOM_VARIANT, nms -> MushroomCow.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.cow.MushroomCow.Variant.values()[api.ordinal()]); - register(DataComponents.RABBIT_VARIANT, nms -> Rabbit.Type.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.rabbit.Rabbit.Variant.byId(api.ordinal())); - register(DataComponents.PIG_VARIANT, CraftPig.CraftVariant::minecraftHolderToBukkit, CraftPig.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.COW_VARIANT, CraftCow.CraftVariant::minecraftHolderToBukkit, CraftCow.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.CHICKEN_VARIANT, nms -> CraftChicken.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftChicken.CraftVariant.bukkitToMinecraftHolder(api))); - register(DataComponents.FROG_VARIANT, CraftFrog.CraftVariant::minecraftHolderToBukkit, CraftFrog.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.ZOMBIE_NAUTILUS_VARIANT, nms -> CraftZombieNautilus.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftZombieNautilus.CraftVariant.bukkitToMinecraftHolder(api))); - register(DataComponents.HORSE_VARIANT, nms -> Horse.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Variant.byId(api.ordinal())); - register(DataComponents.PAINTING_VARIANT, CraftArt::minecraftHolderToBukkit, CraftArt::bukkitToMinecraftHolder); - register(DataComponents.LLAMA_VARIANT, nms -> Llama.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Llama.Variant.byId(api.ordinal())); - register(DataComponents.AXOLOTL_VARIANT, nms -> Axolotl.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.axolotl.Axolotl.Variant.byId(api.ordinal())); - register(DataComponents.CAT_VARIANT, CraftCat.CraftType::minecraftHolderToBukkit, CraftCat.CraftType::bukkitToMinecraftHolder); - register(DataComponents.CAT_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.SHEEP_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - register(DataComponents.SHULKER_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); - - for (final ResourceKey> key : BuiltInRegistries.DATA_COMPONENT_TYPE.registryKeySet()) { - if (!ADAPTERS.containsKey(key)) { - registerUnimplemented(key); - } - } - } - - private static ResourceKey> getKey(final DataComponentType type) { - return BuiltInRegistries.DATA_COMPONENT_TYPE.getResourceKey(type).orElseThrow(); - } - - public static void registerUntyped(final DataComponentType type) { - registerInternal(getKey(type), UNIT_TO_API_CONVERTER, DataComponentAdapter.API_TO_UNIT_CONVERTER, false); - } - - private static void registerIdentity(final DataComponentType type) { - registerInternal(getKey(type), Function.identity(), Function.identity(), true); - } - - @SuppressWarnings("unchecked") - public static void registerUnimplemented(final ResourceKey> key) { - registerInternal(key, UNIMPLEMENTED_TO_API_CONVERTER, DataComponentAdapter.API_TO_UNIMPLEMENTED_CONVERTER, false); - } - - private static > void register(final DataComponentType type, final Function vanillaToApi) { - registerInternal(getKey(type), vanillaToApi, Handleable::getHandle, false); - } - - private static void register(final DataComponentType type, final Function vanillaToApi, final Function apiToVanilla) { - registerInternal(getKey(type), vanillaToApi, apiToVanilla, false); - } - - private static void registerInternal(final ResourceKey> key, final Function vanillaToApi, final Function apiToVanilla, final boolean codecValidation) { - if (ADAPTERS.containsKey(key)) { - throw new IllegalStateException("Duplicate adapter registration for " + key); - } - ADAPTERS.put(key, new DataComponentAdapter<>(apiToVanilla, vanillaToApi, codecValidation)); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java index f2a31526c56c..31eed72cbaca 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java @@ -1,20 +1,210 @@ package io.papermc.paper.datacomponent; +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.datacomponent.item.PaperAttackRange; +import io.papermc.paper.datacomponent.item.PaperBannerPatternLayers; +import io.papermc.paper.datacomponent.item.PaperBlockItemDataProperties; +import io.papermc.paper.datacomponent.item.PaperBlocksAttacks; +import io.papermc.paper.datacomponent.item.PaperBundleContents; +import io.papermc.paper.datacomponent.item.PaperChargedProjectiles; +import io.papermc.paper.datacomponent.item.PaperConsumable; +import io.papermc.paper.datacomponent.item.PaperCustomModelData; +import io.papermc.paper.datacomponent.item.PaperDamageResistant; +import io.papermc.paper.datacomponent.item.PaperDeathProtection; +import io.papermc.paper.datacomponent.item.PaperDyedItemColor; +import io.papermc.paper.datacomponent.item.PaperEnchantable; +import io.papermc.paper.datacomponent.item.PaperEquippable; +import io.papermc.paper.datacomponent.item.PaperFireworks; +import io.papermc.paper.datacomponent.item.PaperFoodProperties; +import io.papermc.paper.datacomponent.item.PaperItemAdventurePredicate; +import io.papermc.paper.datacomponent.item.PaperItemArmorTrim; +import io.papermc.paper.datacomponent.item.PaperItemAttributeModifiers; +import io.papermc.paper.datacomponent.item.PaperItemContainerContents; +import io.papermc.paper.datacomponent.item.PaperItemEnchantments; +import io.papermc.paper.datacomponent.item.PaperItemLore; +import io.papermc.paper.datacomponent.item.PaperItemTool; +import io.papermc.paper.datacomponent.item.PaperJukeboxPlayable; +import io.papermc.paper.datacomponent.item.PaperKineticWeapon; +import io.papermc.paper.datacomponent.item.PaperLodestoneTracker; +import io.papermc.paper.datacomponent.item.PaperMapDecorations; +import io.papermc.paper.datacomponent.item.PaperMapId; +import io.papermc.paper.datacomponent.item.PaperMapItemColor; +import io.papermc.paper.datacomponent.item.PaperOminousBottleAmplifier; +import io.papermc.paper.datacomponent.item.PaperPiercingWeapon; +import io.papermc.paper.datacomponent.item.PaperPotDecorations; +import io.papermc.paper.datacomponent.item.PaperPotionContents; +import io.papermc.paper.datacomponent.item.PaperRepairable; +import io.papermc.paper.datacomponent.item.PaperResolvableProfile; +import io.papermc.paper.datacomponent.item.PaperSeededContainerLoot; +import io.papermc.paper.datacomponent.item.PaperSuspiciousStewEffects; +import io.papermc.paper.datacomponent.item.PaperSwingAnimation; +import io.papermc.paper.datacomponent.item.PaperTooltipDisplay; +import io.papermc.paper.datacomponent.item.PaperUseCooldown; +import io.papermc.paper.datacomponent.item.PaperUseEffects; +import io.papermc.paper.datacomponent.item.PaperUseRemainder; +import io.papermc.paper.datacomponent.item.PaperWeapon; +import io.papermc.paper.datacomponent.item.PaperWritableBookContent; +import io.papermc.paper.datacomponent.item.PaperWrittenBookContent; import io.papermc.paper.registry.HolderableBase; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapters; import java.util.Collections; import java.util.HashSet; import java.util.Set; import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; +import net.minecraft.world.item.EitherHolder; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.item.component.InstrumentComponent; +import net.minecraft.world.item.component.MapPostProcessing; +import net.minecraft.world.item.component.ProvidesTrimMaterial; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftArt; +import org.bukkit.craftbukkit.CraftMusicInstrument; import org.bukkit.craftbukkit.CraftRegistry; +import org.bukkit.craftbukkit.damage.CraftDamageType; +import org.bukkit.craftbukkit.entity.CraftCat; +import org.bukkit.craftbukkit.entity.CraftChicken; +import org.bukkit.craftbukkit.entity.CraftCow; +import org.bukkit.craftbukkit.entity.CraftFrog; +import org.bukkit.craftbukkit.entity.CraftPig; +import org.bukkit.craftbukkit.entity.CraftVillager; +import org.bukkit.craftbukkit.entity.CraftWolf; +import org.bukkit.craftbukkit.entity.CraftZombieNautilus; +import org.bukkit.craftbukkit.inventory.CraftMetaFirework; +import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; +import org.bukkit.entity.Axolotl; +import org.bukkit.entity.Horse; +import org.bukkit.entity.Llama; +import org.bukkit.entity.MushroomCow; +import org.bukkit.entity.Parrot; +import org.bukkit.entity.Rabbit; +import org.bukkit.entity.Salmon; +import org.bukkit.entity.TropicalFish; +import org.bukkit.inventory.ItemRarity; import org.jspecify.annotations.Nullable; +import static io.papermc.paper.util.MCUtil.transformUnmodifiable; + public abstract class PaperDataComponentType extends HolderableBase> implements DataComponentType { - static { - DataComponentAdapters.bootstrap(); - } + private static final PaperTypedDataAdapters ADAPTERS = PaperTypedDataAdapters.create( + BuiltInRegistries.DATA_COMPONENT_TYPE, + PaperDataComponentTypeCollector::new, + collector -> { + collector.registerIdentity(DataComponents.MAX_STACK_SIZE, net.minecraft.core.component.DataComponentType::codec); + collector.registerIdentity(DataComponents.MAX_DAMAGE, net.minecraft.core.component.DataComponentType::codec); + collector.registerIdentity(DataComponents.DAMAGE, net.minecraft.core.component.DataComponentType::codec); + collector.registerUntyped(DataComponents.UNBREAKABLE); + collector.register(DataComponents.USE_EFFECTS, PaperUseEffects::new); + collector.registerIdentity(DataComponents.POTION_DURATION_SCALE, net.minecraft.core.component.DataComponentType::codec); + collector.register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.registerIdentity(DataComponents.MINIMUM_ATTACK_CHARGE, net.minecraft.core.component.DataComponentType::codec); + collector.register(DataComponents.DAMAGE_TYPE, nms -> CraftDamageType.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftDamageType.bukkitToMinecraftHolder(api))); + collector.register(DataComponents.ITEM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.register(DataComponents.ITEM_MODEL, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.register(DataComponents.LORE, PaperItemLore::new); + collector.register(DataComponents.RARITY, nms -> ItemRarity.valueOf(nms.name()), api -> Rarity.valueOf(api.name())); + collector.register(DataComponents.ENCHANTMENTS, PaperItemEnchantments::new); + collector.register(DataComponents.CAN_PLACE_ON, PaperItemAdventurePredicate::new); + collector.register(DataComponents.CAN_BREAK, PaperItemAdventurePredicate::new); + collector.register(DataComponents.ATTRIBUTE_MODIFIERS, PaperItemAttributeModifiers::new); + collector.register(DataComponents.CUSTOM_MODEL_DATA, PaperCustomModelData::new); + collector.registerIdentity(DataComponents.REPAIR_COST, net.minecraft.core.component.DataComponentType::codec); + // registerUntyped(DataComponents.CREATIVE_SLOT_LOCK); + collector.registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, net.minecraft.core.component.DataComponentType::codec); + collector.registerUntyped(DataComponents.INTANGIBLE_PROJECTILE); + collector.register(DataComponents.FOOD, PaperFoodProperties::new); + collector.register(DataComponents.CONSUMABLE, PaperConsumable::new); + collector.register(DataComponents.USE_REMAINDER, PaperUseRemainder::new); + collector.register(DataComponents.USE_COOLDOWN, PaperUseCooldown::new); + collector.register(DataComponents.DAMAGE_RESISTANT, PaperDamageResistant::new); + collector.register(DataComponents.TOOL, PaperItemTool::new); + collector.register(DataComponents.ENCHANTABLE, PaperEnchantable::new); + collector.register(DataComponents.EQUIPPABLE, PaperEquippable::new); + collector.register(DataComponents.REPAIRABLE, PaperRepairable::new); + collector.registerUntyped(DataComponents.GLIDER); + collector.register(DataComponents.TOOLTIP_STYLE, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.register(DataComponents.DEATH_PROTECTION, PaperDeathProtection::new); + collector.register(DataComponents.STORED_ENCHANTMENTS, PaperItemEnchantments::new); + collector.register(DataComponents.DYED_COLOR, PaperDyedItemColor::new); + collector.register(DataComponents.MAP_COLOR, PaperMapItemColor::new); + collector.register(DataComponents.MAP_ID, PaperMapId::new); + collector.register(DataComponents.MAP_DECORATIONS, PaperMapDecorations::new); + collector.register(DataComponents.MAP_POST_PROCESSING, nms -> io.papermc.paper.item.MapPostProcessing.valueOf(nms.name()), api -> MapPostProcessing.valueOf(api.name())); + collector.register(DataComponents.CHARGED_PROJECTILES, PaperChargedProjectiles::new); + collector.register(DataComponents.BUNDLE_CONTENTS, PaperBundleContents::new); + collector.register(DataComponents.POTION_CONTENTS, PaperPotionContents::new); + collector.register(DataComponents.SUSPICIOUS_STEW_EFFECTS, PaperSuspiciousStewEffects::new); + collector.register(DataComponents.WRITTEN_BOOK_CONTENT, PaperWrittenBookContent::new); + collector.register(DataComponents.WRITABLE_BOOK_CONTENT, PaperWritableBookContent::new); + collector.register(DataComponents.TRIM, PaperItemArmorTrim::new); + // debug stick state + // entity data + // bucket entity data + // block entity data + collector.register(DataComponents.INSTRUMENT, nms -> CraftMusicInstrument.minecraftHolderToBukkit(nms.instrument().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new InstrumentComponent(CraftMusicInstrument.bukkitToMinecraftHolder(api))); + collector.register(DataComponents.PROVIDES_TRIM_MATERIAL, nms -> CraftTrimMaterial.minecraftHolderToBukkit(nms.material().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new ProvidesTrimMaterial(CraftTrimMaterial.bukkitToMinecraftHolder(api))); + collector.register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new); + collector.register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new); + collector.register(DataComponents.PROVIDES_BANNER_PATTERNS, PaperRegistries::fromNms, PaperRegistries::toNms); + collector.register( + DataComponents.RECIPES, + nms -> transformUnmodifiable(nms, PaperAdventure::asAdventureKey), + api -> transformUnmodifiable(api, key -> PaperAdventure.asVanilla(Registries.RECIPE, key)) + ); + collector.register(DataComponents.LODESTONE_TRACKER, PaperLodestoneTracker::new); + collector.register(DataComponents.FIREWORK_EXPLOSION, CraftMetaFirework::getEffect, CraftMetaFirework::getExplosion); + collector.register(DataComponents.FIREWORKS, PaperFireworks::new); + collector.register(DataComponents.PROFILE, PaperResolvableProfile::new); + collector.register(DataComponents.NOTE_BLOCK_SOUND, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.register(DataComponents.BANNER_PATTERNS, PaperBannerPatternLayers::new); + collector.register(DataComponents.BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.POT_DECORATIONS, PaperPotDecorations::new); + collector.register(DataComponents.CONTAINER, PaperItemContainerContents::new); + collector.register(DataComponents.BLOCK_STATE, PaperBlockItemDataProperties::new); + // bees + // register(DataComponents.LOCK, PaperLockCode::new); + collector.register(DataComponents.CONTAINER_LOOT, PaperSeededContainerLoot::new); + collector.register(DataComponents.BREAK_SOUND, nms -> PaperAdventure.asAdventure(nms.value().location()), PaperAdventure::resolveSound); + collector.register(DataComponents.TOOLTIP_DISPLAY, PaperTooltipDisplay::new); + collector.register(DataComponents.WEAPON, PaperWeapon::new); + collector.register(DataComponents.BLOCKS_ATTACKS, PaperBlocksAttacks::new); + collector.register(DataComponents.PIERCING_WEAPON, PaperPiercingWeapon::new); + collector.register(DataComponents.KINETIC_WEAPON, PaperKineticWeapon::new); + collector.register(DataComponents.ATTACK_RANGE, PaperAttackRange::new); + collector.register(DataComponents.SWING_ANIMATION, PaperSwingAnimation::new); + collector.register(DataComponents.VILLAGER_VARIANT, CraftVillager.CraftType::minecraftHolderToBukkit, CraftVillager.CraftType::bukkitToMinecraftHolder); + collector.register(DataComponents.WOLF_VARIANT, CraftWolf.CraftVariant::minecraftHolderToBukkit, CraftWolf.CraftVariant::bukkitToMinecraftHolder); + collector.register(DataComponents.WOLF_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.WOLF_SOUND_VARIANT, CraftWolf.CraftSoundVariant::minecraftHolderToBukkit, CraftWolf.CraftSoundVariant::bukkitToMinecraftHolder); + collector.register(DataComponents.FOX_VARIANT, nms -> org.bukkit.entity.Fox.Type.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fox.Fox.Variant.byId(api.ordinal())); + collector.register(DataComponents.SALMON_SIZE, nms -> Salmon.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fish.Salmon.Variant.values()[api.ordinal()]); + collector.register(DataComponents.PARROT_VARIANT, nms -> Parrot.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.parrot.Parrot.Variant.byId(api.ordinal())); + collector.register(DataComponents.TROPICAL_FISH_PATTERN, nms -> TropicalFish.Pattern.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.fish.TropicalFish.Pattern.values()[api.ordinal()]); + collector.register(DataComponents.TROPICAL_FISH_BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.TROPICAL_FISH_PATTERN_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.MOOSHROOM_VARIANT, nms -> MushroomCow.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.cow.MushroomCow.Variant.values()[api.ordinal()]); + collector.register(DataComponents.RABBIT_VARIANT, nms -> Rabbit.Type.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.rabbit.Rabbit.Variant.byId(api.ordinal())); + collector.register(DataComponents.PIG_VARIANT, CraftPig.CraftVariant::minecraftHolderToBukkit, CraftPig.CraftVariant::bukkitToMinecraftHolder); + collector.register(DataComponents.COW_VARIANT, CraftCow.CraftVariant::minecraftHolderToBukkit, CraftCow.CraftVariant::bukkitToMinecraftHolder); + collector.register(DataComponents.CHICKEN_VARIANT, nms -> CraftChicken.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftChicken.CraftVariant.bukkitToMinecraftHolder(api))); + collector.register(DataComponents.FROG_VARIANT, CraftFrog.CraftVariant::minecraftHolderToBukkit, CraftFrog.CraftVariant::bukkitToMinecraftHolder); + collector.register(DataComponents.ZOMBIE_NAUTILUS_VARIANT, nms -> CraftZombieNautilus.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftZombieNautilus.CraftVariant.bukkitToMinecraftHolder(api))); + collector.register(DataComponents.HORSE_VARIANT, nms -> Horse.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Variant.byId(api.ordinal())); + collector.register(DataComponents.PAINTING_VARIANT, CraftArt::minecraftHolderToBukkit, CraftArt::bukkitToMinecraftHolder); + collector.register(DataComponents.LLAMA_VARIANT, nms -> Llama.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Llama.Variant.byId(api.ordinal())); + collector.register(DataComponents.AXOLOTL_VARIANT, nms -> Axolotl.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.axolotl.Axolotl.Variant.byId(api.ordinal())); + collector.register(DataComponents.CAT_VARIANT, CraftCat.CraftType::minecraftHolderToBukkit, CraftCat.CraftType::bukkitToMinecraftHolder); + collector.register(DataComponents.CAT_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.SHEEP_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + collector.register(DataComponents.SHULKER_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); + } + ); public static net.minecraft.core.component.DataComponentType bukkitToMinecraft(final DataComponentType type) { return CraftRegistry.bukkitToMinecraft(type); @@ -41,9 +231,9 @@ public static Set minecraftToBukkit(final Set adapter; + private final PaperTypedDataAdapter adapter; - private PaperDataComponentType(final Holder> holder, final DataComponentAdapter adapter) { + private PaperDataComponentType(final Holder> holder, final PaperTypedDataAdapter adapter) { super(holder); this.adapter = adapter; } @@ -53,13 +243,13 @@ public boolean isPersistent() { return !this.getHandle().isTransient(); } - public DataComponentAdapter getAdapter() { + public PaperTypedDataAdapter getAdapter() { return this.adapter; } - @SuppressWarnings({"unchecked"}) + @SuppressWarnings("unchecked") public static DataComponentType of(final Holder holder) { - final DataComponentAdapter adapter = (DataComponentAdapter) DataComponentAdapters.ADAPTERS.get(holder.unwrapKey().orElseThrow()); + final PaperTypedDataAdapter< NMS, ?> adapter = PaperDataComponentType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); if (adapter == null) { throw new IllegalArgumentException("No adapter found for " + holder); } @@ -76,7 +266,7 @@ public static final class NonValuedImpl extends PaperDataComponentType> holder, - final DataComponentAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } @@ -86,7 +276,7 @@ public static final class ValuedImpl extends PaperDataComponentType> holder, - final DataComponentAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } @@ -96,7 +286,7 @@ public static final class Unimplemented extends PaperDataComponentType> holder, - final DataComponentAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentTypeCollector.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentTypeCollector.java new file mode 100644 index 000000000000..435231edc5ec --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentTypeCollector.java @@ -0,0 +1,27 @@ +package io.papermc.paper.datacomponent; + +import io.papermc.paper.registry.data.typed.AbstractTypedDataCollector; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.resources.ResourceKey; +import org.bukkit.craftbukkit.util.Handleable; +import java.util.Map; +import java.util.function.Function; + +class PaperDataComponentTypeCollector extends AbstractTypedDataCollector> { + + public PaperDataComponentTypeCollector(final Registry> registry, final Map, PaperTypedDataAdapter> adapters) { + super(registry, adapters); + } + + // Not using @Override because of generic types + public > void register(final DataComponentType dataComponentType, final Function vanillaToApi) { + super.register(dataComponentType, vanillaToApi); + } + + // Not using @Override because of generic types + public void register(final DataComponentType dataComponentType, final Function vanillaToApi, final Function apiToVanilla) { + super.register(dataComponentType, vanillaToApi, apiToVanilla); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java new file mode 100644 index 000000000000..e8bf7ad49b86 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java @@ -0,0 +1,75 @@ +package io.papermc.paper.registry.data.typed; + +import com.mojang.serialization.Codec; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import org.bukkit.craftbukkit.util.Handleable; +import org.jspecify.annotations.Nullable; + +// This class exists only to be implemented, implementations must override the following register method: +// void register(final TYPE type, final Function vanillaToApi, final Function apiToVanilla) +// BUT should NOT use the @Override annotation, this is a hack around generics limitations to prevent +// having to define generics on each register call as seen below, making collectors easier to read: +// collector.register(...) +public abstract class AbstractTypedDataCollector implements PaperTypedDataCollector { + + private final Registry registry; + private final Map, PaperTypedDataAdapter> adapters; + + public AbstractTypedDataCollector(final Registry registry, final Map, PaperTypedDataAdapter> adapters) { + this.registry = registry; + this.adapters = adapters; + } + + @Override + public void registerUntyped(final TYPE type) { + this.registerInternal(this.getKey(type), PaperTypedDataAdapters.UNIT_TO_API_CONVERTER, PaperTypedDataAdapter.API_TO_UNIT_CONVERTER, null); + } + + @Override + public void registerIdentity(final TYPE type, final Function> codecGetter) { + this.registerInternal(this.getKey(type), Function.identity(), Function.identity(), codecGetter.apply(type)); + } + + @Override + public > void register(final TYPE type, final Function vanillaToApi) { + this.registerInternal(this.getKey(type), vanillaToApi, Handleable::getHandle, null); + } + + @Override + public void register(final TYPE type, final Function vanillaToApi, final Function apiToVanilla) { + this.registerInternal(this.getKey(type), vanillaToApi, apiToVanilla, null); + } + + private ResourceKey getKey(final TYPE type) { + return this.registry.getResourceKey(type).orElseThrow(); + } + + void registerUnimplemented() { + for (final ResourceKey key : this.registry.registryKeySet()) { + if (!this.adapters.containsKey(key)) { + this.registerUnimplemented(key); + } + } + } + + @SuppressWarnings("unchecked") + private void registerUnimplemented(final ResourceKey key) { + this.registerInternal(key, PaperTypedDataAdapters.UNIMPLEMENTED_TO_API_CONVERTER, PaperTypedDataAdapter.API_TO_UNIMPLEMENTED_CONVERTER, null); + } + + private void registerInternal( + final ResourceKey key, + final Function vanillaToApi, + final Function apiToVanilla, + final @Nullable Codec codec + ) { + if (this.adapters.containsKey(key)) { + throw new IllegalStateException("Duplicate adapter registration for " + key); + } + + this.adapters.put(key, new PaperTypedDataAdapter<>(apiToVanilla, vanillaToApi, codec)); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java similarity index 69% rename from paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java rename to paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java index 15a96abb2ac7..eb978dfaa5cd 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java @@ -1,16 +1,17 @@ -package io.papermc.paper.datacomponent; +package io.papermc.paper.registry.data.typed; +import com.mojang.serialization.Codec; import java.util.function.Function; import net.minecraft.core.Holder; -import net.minecraft.core.component.DataComponentType; import net.minecraft.util.NullOps; import net.minecraft.util.Unit; import org.bukkit.craftbukkit.CraftRegistry; +import org.jspecify.annotations.Nullable; -public record DataComponentAdapter( +public record PaperTypedDataAdapter( Function apiToVanilla, Function vanillaToApi, - boolean codecValidation + @Nullable Codec codec ) { static final Function API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE; @@ -26,10 +27,10 @@ public boolean isUnimplemented() { return this.apiToVanilla == API_TO_UNIMPLEMENTED_CONVERTER; } - public NMS toVanilla(final API value, final Holder> type) { + public NMS toVanilla(final API value, final Holder type) { final NMS nms = this.apiToVanilla.apply(value); - if (this.codecValidation && !type.value().isTransient()) { - type.value().codecOrThrow().encodeStart(CraftRegistry.getMinecraftRegistry().createSerializationContext(NullOps.INSTANCE), nms).ifError(error -> { + if (this.codec != null) { + this.codec.encodeStart(CraftRegistry.getMinecraftRegistry().createSerializationContext(NullOps.INSTANCE), nms).ifError(error -> { throw new IllegalArgumentException("Failed to encode data component %s (%s)".formatted(type.unwrapKey().orElseThrow(), error.message())); }); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapters.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapters.java new file mode 100644 index 000000000000..bc2f442981c0 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapters.java @@ -0,0 +1,49 @@ +package io.papermc.paper.registry.data.typed; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.Unit; +import org.jspecify.annotations.Nullable; + +public final class PaperTypedDataAdapters { + + static final Function UNIT_TO_API_CONVERTER = $ -> { + throw new UnsupportedOperationException("Cannot convert the Unit type to an API value"); + }; + + static final Function UNIMPLEMENTED_TO_API_CONVERTER = $ -> { + throw new UnsupportedOperationException("Cannot convert the an unimplemented type to an API value"); + }; + + private final Map, PaperTypedDataAdapter> adapters; + + private PaperTypedDataAdapters(Map, PaperTypedDataAdapter> adapters) { + this.adapters = adapters; + } + + public static > PaperTypedDataAdapters create( + Registry registry, + PaperTypedDataCollector.Factory collectorFactory, + Consumer consumer + ) { + Map, PaperTypedDataAdapter> adapters = new HashMap<>(); + COLLECTOR collector = collectorFactory.create(registry, adapters); + + consumer.accept(collector); + + // Loop through every entry in the registry and register any entry + // not already registered as unimplemented + collector.registerUnimplemented(); + + return new PaperTypedDataAdapters(adapters); + } + + @SuppressWarnings("unchecked") + public @Nullable RETURN getAdapter(ResourceKey key) { + return (RETURN) this.adapters.get(key); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataCollector.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataCollector.java new file mode 100644 index 000000000000..ed24fc910f58 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataCollector.java @@ -0,0 +1,26 @@ +package io.papermc.paper.registry.data.typed; + +import com.mojang.serialization.Codec; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import org.bukkit.craftbukkit.util.Handleable; +import org.jspecify.annotations.Nullable; + +public interface PaperTypedDataCollector { + + void registerUntyped(final TYPE type); + + void registerIdentity(final TYPE type, final Function> codecGetter); + + > void register(final TYPE type, final Function vanillaToApi); + + void register(final TYPE type, final Function vanillaToApi, final Function apiToVanilla); + + interface Factory { + + COLLECTOR create(Registry registry, Map, PaperTypedDataAdapter> adapters); + + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/package-info.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/package-info.java new file mode 100644 index 000000000000..535e3cb038cd --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.registry.data.typed; + +import org.jspecify.annotations.NullMarked; From 9098d704a522e60db3466e463a46ec47ac40106b Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Sat, 29 Nov 2025 17:36:18 +0100 Subject: [PATCH 2/6] wip: environmental attributes api --- .../keys/EnvironmentAttributeKeys.java | 349 ++++++++++++++++++ .../papermc/paper/registry/RegistryKey.java | 6 + .../attribute/EnvironmentalAttributeType.java | 14 + .../EnvironmentalAttributeTypes.java | 122 ++++++ .../generator/registry/RegistryEntries.java | 5 +- .../paper/adventure/PaperAdventure.java | 18 + .../paper/registry/PaperRegistries.java | 3 + .../PaperEnvironmentalAttributeType.java | 100 +++++ ...erEnvironmentalAttributeTypeCollector.java | 27 ++ 9 files changed, 643 insertions(+), 1 deletion(-) create mode 100644 paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentAttributeKeys.java create mode 100644 paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java create mode 100644 paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeTypes.java create mode 100644 paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java create mode 100644 paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeTypeCollector.java diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentAttributeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentAttributeKeys.java new file mode 100644 index 000000000000..f3c10d82ae0f --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentAttributeKeys.java @@ -0,0 +1,349 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#ENVIRONMENT_ATTRIBUTE}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class EnvironmentAttributeKeys { + /** + * {@code minecraft:audio/ambient_sounds} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> AUDIO_AMBIENT_SOUNDS = create(key("audio/ambient_sounds")); + + /** + * {@code minecraft:audio/background_music} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> AUDIO_BACKGROUND_MUSIC = create(key("audio/background_music")); + + /** + * {@code minecraft:audio/firefly_bush_sounds} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> AUDIO_FIREFLY_BUSH_SOUNDS = create(key("audio/firefly_bush_sounds")); + + /** + * {@code minecraft:audio/music_volume} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> AUDIO_MUSIC_VOLUME = create(key("audio/music_volume")); + + /** + * {@code minecraft:gameplay/baby_villager_activity} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_BABY_VILLAGER_ACTIVITY = create(key("gameplay/baby_villager_activity")); + + /** + * {@code minecraft:gameplay/bed_rule} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_BED_RULE = create(key("gameplay/bed_rule")); + + /** + * {@code minecraft:gameplay/bees_stay_in_hive} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_BEES_STAY_IN_HIVE = create(key("gameplay/bees_stay_in_hive")); + + /** + * {@code minecraft:gameplay/can_pillager_patrol_spawn} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_CAN_PILLAGER_PATROL_SPAWN = create(key("gameplay/can_pillager_patrol_spawn")); + + /** + * {@code minecraft:gameplay/can_start_raid} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_CAN_START_RAID = create(key("gameplay/can_start_raid")); + + /** + * {@code minecraft:gameplay/cat_waking_up_gift_chance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_CAT_WAKING_UP_GIFT_CHANCE = create(key("gameplay/cat_waking_up_gift_chance")); + + /** + * {@code minecraft:gameplay/creaking_active} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_CREAKING_ACTIVE = create(key("gameplay/creaking_active")); + + /** + * {@code minecraft:gameplay/eyeblossom_open} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_EYEBLOSSOM_OPEN = create(key("gameplay/eyeblossom_open")); + + /** + * {@code minecraft:gameplay/fast_lava} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_FAST_LAVA = create(key("gameplay/fast_lava")); + + /** + * {@code minecraft:gameplay/increased_fire_burnout} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_INCREASED_FIRE_BURNOUT = create(key("gameplay/increased_fire_burnout")); + + /** + * {@code minecraft:gameplay/monsters_burn} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_MONSTERS_BURN = create(key("gameplay/monsters_burn")); + + /** + * {@code minecraft:gameplay/nether_portal_spawns_piglin} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_NETHER_PORTAL_SPAWNS_PIGLIN = create(key("gameplay/nether_portal_spawns_piglin")); + + /** + * {@code minecraft:gameplay/piglins_zombify} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_PIGLINS_ZOMBIFY = create(key("gameplay/piglins_zombify")); + + /** + * {@code minecraft:gameplay/respawn_anchor_works} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_RESPAWN_ANCHOR_WORKS = create(key("gameplay/respawn_anchor_works")); + + /** + * {@code minecraft:gameplay/sky_light_level} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_SKY_LIGHT_LEVEL = create(key("gameplay/sky_light_level")); + + /** + * {@code minecraft:gameplay/snow_golem_melts} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_SNOW_GOLEM_MELTS = create(key("gameplay/snow_golem_melts")); + + /** + * {@code minecraft:gameplay/surface_slime_spawn_chance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_SURFACE_SLIME_SPAWN_CHANCE = create(key("gameplay/surface_slime_spawn_chance")); + + /** + * {@code minecraft:gameplay/turtle_egg_hatch_chance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_TURTLE_EGG_HATCH_CHANCE = create(key("gameplay/turtle_egg_hatch_chance")); + + /** + * {@code minecraft:gameplay/villager_activity} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_VILLAGER_ACTIVITY = create(key("gameplay/villager_activity")); + + /** + * {@code minecraft:gameplay/water_evaporates} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> GAMEPLAY_WATER_EVAPORATES = create(key("gameplay/water_evaporates")); + + /** + * {@code minecraft:visual/ambient_particles} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_AMBIENT_PARTICLES = create(key("visual/ambient_particles")); + + /** + * {@code minecraft:visual/cloud_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_CLOUD_COLOR = create(key("visual/cloud_color")); + + /** + * {@code minecraft:visual/cloud_fog_end_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_CLOUD_FOG_END_DISTANCE = create(key("visual/cloud_fog_end_distance")); + + /** + * {@code minecraft:visual/cloud_height} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_CLOUD_HEIGHT = create(key("visual/cloud_height")); + + /** + * {@code minecraft:visual/default_dripstone_particle} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_DEFAULT_DRIPSTONE_PARTICLE = create(key("visual/default_dripstone_particle")); + + /** + * {@code minecraft:visual/fog_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_FOG_COLOR = create(key("visual/fog_color")); + + /** + * {@code minecraft:visual/fog_end_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_FOG_END_DISTANCE = create(key("visual/fog_end_distance")); + + /** + * {@code minecraft:visual/fog_start_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_FOG_START_DISTANCE = create(key("visual/fog_start_distance")); + + /** + * {@code minecraft:visual/moon_angle} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_MOON_ANGLE = create(key("visual/moon_angle")); + + /** + * {@code minecraft:visual/moon_phase} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_MOON_PHASE = create(key("visual/moon_phase")); + + /** + * {@code minecraft:visual/sky_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SKY_COLOR = create(key("visual/sky_color")); + + /** + * {@code minecraft:visual/sky_fog_end_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SKY_FOG_END_DISTANCE = create(key("visual/sky_fog_end_distance")); + + /** + * {@code minecraft:visual/sky_light_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SKY_LIGHT_COLOR = create(key("visual/sky_light_color")); + + /** + * {@code minecraft:visual/sky_light_factor} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SKY_LIGHT_FACTOR = create(key("visual/sky_light_factor")); + + /** + * {@code minecraft:visual/star_angle} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_STAR_ANGLE = create(key("visual/star_angle")); + + /** + * {@code minecraft:visual/star_brightness} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_STAR_BRIGHTNESS = create(key("visual/star_brightness")); + + /** + * {@code minecraft:visual/sun_angle} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SUN_ANGLE = create(key("visual/sun_angle")); + + /** + * {@code minecraft:visual/sunrise_sunset_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_SUNRISE_SUNSET_COLOR = create(key("visual/sunrise_sunset_color")); + + /** + * {@code minecraft:visual/water_fog_color} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_WATER_FOG_COLOR = create(key("visual/water_fog_color")); + + /** + * {@code minecraft:visual/water_fog_end_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_WATER_FOG_END_DISTANCE = create(key("visual/water_fog_end_distance")); + + /** + * {@code minecraft:visual/water_fog_start_distance} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey> VISUAL_WATER_FOG_START_DISTANCE = create(key("visual/water_fog_start_distance")); + + private EnvironmentAttributeKeys() { + } + + private static TypedKey> create(final Key key) { + return TypedKey.create(RegistryKey.ENVIRONMENT_ATTRIBUTE, key); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index dcee3d88f5b7..5371ebebbf6f 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -3,6 +3,7 @@ import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import net.kyori.adventure.key.Key; import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; @@ -132,6 +133,11 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.GameRuleKeys */ RegistryKey> GAME_RULE = create("game_rule"); + /** + * Built-in registry for environmental attribute types. + * @see io.papermc.paper.registry.keys.EnvironmentAttributeKeys + */ + RegistryKey> ENVIRONMENT_ATTRIBUTE = create("environment_attribute"); /* ********************** * * Data-driven Registries * diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java new file mode 100644 index 000000000000..c8f0cf5155fc --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java @@ -0,0 +1,14 @@ +package io.papermc.paper.world.attribute; + +import org.bukkit.Keyed; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface EnvironmentalAttributeType extends Keyed { + + T getDefaultValue(); + +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeTypes.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeTypes.java new file mode 100644 index 000000000000..6dbddffc57d5 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeTypes.java @@ -0,0 +1,122 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.world.MoonPhase; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.util.TriState; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class EnvironmentalAttributeTypes { + + // ================ + // AUDIO + // ================ + + // public static final EnvironmentalAttributeType AMBIENT_SOUNDS = get("audio/ambient_sounds"); + + // public static final EnvironmentalAttributeType BACKGROUND_MUSIC = get("audio/background_music"); + + public static final EnvironmentalAttributeType FIREFLY_BUSH_SOUNDS = get("audio/firefly_bush_sounds"); + + public static final EnvironmentalAttributeType MUSIC_VOLUME = get("audio/music_volume"); + + // ================ + // GAMEPLAY + // ================ + + // public static final EnvironmentalAttributeType BABY_VILLAGER_ACTIVITY = get("gameplay/baby_villager_activity"); + + // public static final EnvironmentalAttributeType BED_RULE = get("gameplay/bed_rule"); + + public static final EnvironmentalAttributeType BEES_STAY_IN_HIVE = get("gameplay/bees_stay_in_hive"); + + public static final EnvironmentalAttributeType CAN_PILLAGER_PATROL_SPAWN = get("gameplay/can_pillager_patrol_spawn"); + + public static final EnvironmentalAttributeType CAN_START_RAID = get("gameplay/can_start_raid"); + + public static final EnvironmentalAttributeType CAT_WAKING_UP_GIFT_CHANCE = get("gameplay/cat_waking_up_gift_chance"); + + public static final EnvironmentalAttributeType CREAKING_ACTIVE = get("gameplay/creaking_active"); + + public static final EnvironmentalAttributeType EYEBLOSSOM_OPEN = get("gameplay/eyeblossom_open"); + + public static final EnvironmentalAttributeType FAST_LAVA = get("gameplay/fast_lava"); + + public static final EnvironmentalAttributeType INCREASED_FIRE_BURNOUT = get("gameplay/increased_fire_burnout"); + + public static final EnvironmentalAttributeType MONSTERS_BURN = get("gameplay/monsters_burn"); + + public static final EnvironmentalAttributeType NETHER_PORTAL_SPAWNS_PIGLIN = get("gameplay/nether_portal_spawns_piglin"); + + public static final EnvironmentalAttributeType PIGLINS_ZOMBIFY = get("gameplay/piglins_zombify"); + + public static final EnvironmentalAttributeType RESPAWN_ANCHOR_WORKS = get("gameplay/respawn_anchor_works"); + + public static final EnvironmentalAttributeType SKY_LIGHT_LEVEL = get("gameplay/sky_light_level"); + + public static final EnvironmentalAttributeType SNOW_GOLEM_MELTS = get("gameplay/snow_golem_melts"); + + public static final EnvironmentalAttributeType SURFACE_SLIME_SPAWN_CHANCE = get("gameplay/surface_slime_spawn_chance"); + + public static final EnvironmentalAttributeType TURTLE_EGG_HATCH_CHANCE = get("gameplay/turtle_egg_hatch_chance"); + + // public static final EnvironmentalAttributeType VILLAGER_ACTIVITY = get("gameplay/villager_activity"); + + public static final EnvironmentalAttributeType WATER_EVAPORATES = get("gameplay/water_evaporates"); + + // ================ + // VISUAL + // ================ + + // public static final EnvironmentalAttributeType> AMBIENT_PARTICLES = get("visual/ambient_particles"); + + public static final EnvironmentalAttributeType CLOUD_COLOR = get("visual/cloud_color"); + + public static final EnvironmentalAttributeType CLOUD_FOG_END_DISTANCE = get("visual/cloud_fog_end_distance"); + + public static final EnvironmentalAttributeType CLOUD_HEIGHT = get("visual/cloud_height"); + + // public static final EnvironmentalAttributeType DEFAULT_DRIPSTONE_PARTICLE = get("visual/default_dripstone_particle"); + + public static final EnvironmentalAttributeType FOG_COLOR = get("visual/fog_color"); + + public static final EnvironmentalAttributeType FOG_END_DISTANCE = get("visual/fog_end_distance"); + + public static final EnvironmentalAttributeType FOG_START_DISTANCE = get("visual/fog_start_distance"); + + public static final EnvironmentalAttributeType MOON_ANGLE = get("visual/moon_angle"); + + public static final EnvironmentalAttributeType MOON_PHASE = get("visual/moon_phase"); + + public static final EnvironmentalAttributeType SKY_COLOR = get("visual/sky_color"); + + public static final EnvironmentalAttributeType SKY_FOG_END_DISTANCE = get("visual/sky_fog_end_distance"); + + public static final EnvironmentalAttributeType SKY_LIGHT_COLOR = get("visual/sky_light_color"); + + public static final EnvironmentalAttributeType SKY_LIGHT_FACTOR = get("visual/sky_light_factor"); + + public static final EnvironmentalAttributeType STAR_ANGLE = get("visual/star_angle"); + + public static final EnvironmentalAttributeType STAR_BRIGHTNESS = get("visual/star_brightness"); + + public static final EnvironmentalAttributeType SUN_ANGLE = get("visual/sun_angle"); + + public static final EnvironmentalAttributeType SUNRISE_SUNSET_COLOR = get("visual/sunrise_sunset_color"); + + public static final EnvironmentalAttributeType WATER_FOG_COLOR = get("visual/water_fog_color"); + + public static final EnvironmentalAttributeType WATER_FOG_END_DISTANCE = get("visual/water_fog_end_distance"); + + public static final EnvironmentalAttributeType WATER_FOG_START_DISTANCE = get("visual/water_fog_start_distance"); + + private EnvironmentalAttributeTypes() { + } + + @SuppressWarnings("unchecked") + private static EnvironmentalAttributeType get(final String key) { + return (EnvironmentalAttributeType) RegistryAccess.registryAccess().getRegistry(RegistryKey.ENVIRONMENT_ATTRIBUTE).getOrThrow(Key.key(key)); + } +} diff --git a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java index 0a9f1e9a68f7..2feb0363f993 100644 --- a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java +++ b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java @@ -32,6 +32,7 @@ import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; @@ -39,6 +40,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.server.dialog.Dialogs; import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -170,7 +172,8 @@ private static RegistryEntry inconsistentEntry(ResourceKey> DATA_DRIVEN = List.of( diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java index 8cd1ea370dd4..13f27635c728 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java @@ -35,6 +35,7 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.util.Codec; +import net.kyori.adventure.util.TriState; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.Holder; @@ -468,4 +469,21 @@ public static Style asAdventure(final net.minecraft.network.chat.Style style) { .parse(ops, encoded).getOrThrow(IllegalStateException::new); } + // TriState + + public static net.minecraft.util.TriState asVanilla(final TriState value) { + return switch (value) { + case TRUE -> net.minecraft.util.TriState.TRUE; + case FALSE -> net.minecraft.util.TriState.FALSE; + case NOT_SET -> net.minecraft.util.TriState.DEFAULT; + }; + } + + public static TriState asAdventure(final net.minecraft.util.TriState value) { + return switch(value) { + case TRUE -> TriState.TRUE; + case FALSE -> TriState.FALSE; + case DEFAULT -> TriState.NOT_SET; + }; + } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 121babcfdda5..02c7bf6d8cc4 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -25,6 +25,8 @@ import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; +import io.papermc.paper.world.attribute.PaperEnvironmentalAttributeType; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -121,6 +123,7 @@ public final class PaperRegistries { start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new, true).create(PaperSoundEventRegistryEntry.PaperBuilder::new, RegistryEntryMeta.RegistryModificationApiSupport.NONE), start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(), start(Registries.GAME_RULE, RegistryKey.GAME_RULE).craft(GameRule.class, CraftGameRule::new).build(), + start(Registries.ENVIRONMENT_ATTRIBUTE, RegistryKey.ENVIRONMENT_ATTRIBUTE).craft(EnvironmentalAttributeType.class, PaperEnvironmentalAttributeType::of).build(), // data-driven start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(), diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java new file mode 100644 index 000000000000..a0a9f076b338 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java @@ -0,0 +1,100 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.HolderableBase; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapters; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.attribute.EnvironmentAttribute; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.level.MoonPhase; + +public final class PaperEnvironmentalAttributeType extends HolderableBase> implements EnvironmentalAttributeType { + + private static final PaperTypedDataAdapters ADAPTERS = PaperTypedDataAdapters.create( + BuiltInRegistries.ENVIRONMENT_ATTRIBUTE, + PaperEnvironmentalAttributeTypeCollector::new, + collector -> { + // Audio + // collector.register(EnvironmentAttributes.AMBIENT_SOUNDS, toApi, toNms); + // collector.register(EnvironmentAttributes.BACKGROUND_MUSIC, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.FIREFLY_BUSH_SOUNDS, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.MUSIC_VOLUME, EnvironmentAttribute::valueCodec); + + // Gameplay + // collector.register(EnvironmentAttributes.BABY_VILLAGER_ACTIVITY, toApi, toNms); + // collector.register(EnvironmentAttributes.BED_RULE, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.BEES_STAY_IN_HIVE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CAN_PILLAGER_PATROL_SPAWN, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CAN_START_RAID, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CAT_WAKING_UP_GIFT_CHANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CREAKING_ACTIVE, EnvironmentAttribute::valueCodec); + collector.register(EnvironmentAttributes.EYEBLOSSOM_OPEN, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.registerIdentity(EnvironmentAttributes.FAST_LAVA, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.INCREASED_FIRE_BURNOUT, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.MONSTERS_BURN, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.PIGLINS_ZOMBIFY, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.RESPAWN_ANCHOR_WORKS, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SKY_LIGHT_LEVEL, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SNOW_GOLEM_MELTS, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SURFACE_SLIME_SPAWN_CHANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.TURTLE_EGG_HATCH_CHANCE, EnvironmentAttribute::valueCodec); + // collector.register(EnvironmentAttributes.VILLAGER_ACTIVITY, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.WATER_EVAPORATES, EnvironmentAttribute::valueCodec); + + // Visual + // collector.register(EnvironmentAttributes.AMBIENT_PARTICLES, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.CLOUD_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CLOUD_FOG_END_DISTANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.CLOUD_HEIGHT, EnvironmentAttribute::valueCodec); + // collector.register(EnvironmentAttributes.DEFAULT_DRIPSTONE_PARTICLE, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.FOG_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.FOG_END_DISTANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.FOG_START_DISTANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.MOON_ANGLE, EnvironmentAttribute::valueCodec); + collector.register(EnvironmentAttributes.MOON_PHASE, moonPhase -> io.papermc.paper.world.MoonPhase.values()[moonPhase.ordinal()], moonPhase -> MoonPhase.values()[moonPhase.ordinal()]); + collector.registerIdentity(EnvironmentAttributes.SKY_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SKY_FOG_END_DISTANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SKY_LIGHT_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SKY_LIGHT_FACTOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.STAR_ANGLE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.STAR_BRIGHTNESS, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SUN_ANGLE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.SUNRISE_SUNSET_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.WATER_FOG_COLOR, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.WATER_FOG_END_DISTANCE, EnvironmentAttribute::valueCodec); + collector.registerIdentity(EnvironmentAttributes.WATER_FOG_START_DISTANCE, EnvironmentAttribute::valueCodec); + } + ); + + private final PaperTypedDataAdapter adapter; + + private PaperEnvironmentalAttributeType(Holder> holder, PaperTypedDataAdapter adapter) { + super(holder); + + this.adapter = adapter; + } + + @SuppressWarnings("unchecked") + public static EnvironmentalAttributeType of(final Holder holder) { + final PaperTypedDataAdapter adapter = PaperEnvironmentalAttributeType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); + if (adapter == null) { + throw new IllegalArgumentException("No adapter found for " + holder); + } + if (!adapter.isValued()) { + throw new IllegalStateException("Non-valued adapter is not supported for environmental attribute types: " + holder); + } + return new PaperEnvironmentalAttributeType<>((Holder>) holder, adapter); + } + + @Override + public API getDefaultValue() { + return this.getAdapter().fromVanilla(this.getHandle().defaultValue()); + } + + public PaperTypedDataAdapter getAdapter() { + return this.adapter; + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeTypeCollector.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeTypeCollector.java new file mode 100644 index 000000000000..30eb1bb69383 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeTypeCollector.java @@ -0,0 +1,27 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.registry.data.typed.AbstractTypedDataCollector; +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.attribute.EnvironmentAttribute; +import org.bukkit.craftbukkit.util.Handleable; + +class PaperEnvironmentalAttributeTypeCollector extends AbstractTypedDataCollector> { + + public PaperEnvironmentalAttributeTypeCollector(final Registry> registry, final Map, PaperTypedDataAdapter> adapters) { + super(registry, adapters); + } + + // Not using @Override because of generic types + public > void register(final EnvironmentAttribute dataComponentType, final Function vanillaToApi) { + super.register(dataComponentType, vanillaToApi); + } + + // Not using @Override because of generic types + public void register(final EnvironmentAttribute dataComponentType, final Function vanillaToApi, final Function apiToVanilla) { + super.register(dataComponentType, vanillaToApi, apiToVanilla); + } +} From 60ccda088f15a19e27f07e2cf53bce1dbfbc0fd8 Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Sat, 29 Nov 2025 17:42:12 +0100 Subject: [PATCH 3/6] wip: update AbstractTypedDataCollector comment --- .../paper/registry/data/typed/AbstractTypedDataCollector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java index e8bf7ad49b86..1444f1864ece 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/AbstractTypedDataCollector.java @@ -8,8 +8,9 @@ import org.bukkit.craftbukkit.util.Handleable; import org.jspecify.annotations.Nullable; -// This class exists only to be implemented, implementations must override the following register method: +// This class exists only to be implemented, implementations must override the following register methods: // void register(final TYPE type, final Function vanillaToApi, final Function apiToVanilla) +// void register(final TYPE type, final Function vanillaToApi) // BUT should NOT use the @Override annotation, this is a hack around generics limitations to prevent // having to define generics on each register call as seen below, making collectors easier to read: // collector.register(...) From a9f79b17ae73fd756e121635727e51cdb66a2db9 Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Sun, 30 Nov 2025 21:32:23 +0100 Subject: [PATCH 4/6] fix: fixed failing tests --- .../org/bukkit/registry/RegistryConstantsTest.java | 10 ++++++++++ .../support/provider/RegistriesArgumentProvider.java | 11 ++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java b/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java index 2718c82e0b6b..9913bd4fe2fe 100644 --- a/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java +++ b/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java @@ -15,6 +15,7 @@ import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.attribute.EnvironmentAttributes; import org.bukkit.Keyed; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.support.RegistryHelper; @@ -58,6 +59,15 @@ public static void populateIgnored() { DataComponents.LOCK, DataComponents.CREATIVE_SLOT_LOCK )); + ignore(Registries.ENVIRONMENT_ATTRIBUTE, Set.of( + EnvironmentAttributes.AMBIENT_SOUNDS, + EnvironmentAttributes.BACKGROUND_MUSIC, + EnvironmentAttributes.BABY_VILLAGER_ACTIVITY, + EnvironmentAttributes.BED_RULE, + EnvironmentAttributes.VILLAGER_ACTIVITY, + EnvironmentAttributes.AMBIENT_PARTICLES, + EnvironmentAttributes.DEFAULT_DRIPSTONE_PARTICLE + )); } public static Stream registries() { diff --git a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java index 00b949fc0982..6e85565ab378 100644 --- a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +++ b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java @@ -6,19 +6,23 @@ import io.papermc.paper.dialog.PaperDialog; import io.papermc.paper.registry.PaperRegistries; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; +import io.papermc.paper.world.attribute.EnvironmentalAttributeTypes; import java.util.List; import java.util.stream.Stream; +import io.papermc.paper.world.attribute.PaperEnvironmentalAttributeType; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.attribute.EnvironmentAttribute; import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.entity.animal.chicken.ChickenVariant; import net.minecraft.world.entity.animal.cow.CowVariant; -import net.minecraft.world.entity.animal.pig.PigVariant; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.entity.animal.frog.FrogVariant; +import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.pig.PigVariant; import net.minecraft.world.entity.animal.wolf.WolfSoundVariant; import net.minecraft.world.entity.animal.wolf.WolfVariant; import net.minecraft.world.entity.decoration.painting.PaintingVariant; @@ -149,6 +153,7 @@ public Object[] get() { register(Registries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant.class, ZombieNautilusVariant.class); register(Registries.DIALOG, Dialog.class, PaperDialog.class, net.minecraft.server.dialog.Dialog.class); register(Registries.GAME_RULE, GameRule.class, GameRules.class, CraftGameRule.class, net.minecraft.world.level.gamerules.GameRule.class); + register(Registries.ENVIRONMENT_ATTRIBUTE, EnvironmentalAttributeType.class, EnvironmentalAttributeTypes.class, PaperEnvironmentalAttributeType.class, EnvironmentAttribute.class); } private static void register(ResourceKey> registryKey, Class api, Class impl, Class internal) { From a40ace11cf0cfd002ae11c308b8a730af6c6b305 Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Sun, 30 Nov 2025 21:33:50 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=C3=83feat:=20environmental=20attribute=20a?= =?UTF-8?q?pi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/papermc/paper/InternalAPIBridge.java | 3 + .../attribute/EnvironmentalAttribute.java | 22 +++++ .../EnvironmentalAttributeContext.java | 43 ++++++++++ .../attribute/EnvironmentalAttributeType.java | 2 - .../paper/world/attribute/package-info.java | 4 + paper-api/src/main/java/org/bukkit/World.java | 4 + .../attribute/WeatherAttributes.java.patch | 17 ++++ .../timeline/AttributeTrackSampler.java.patch | 11 +++ .../paper/PaperServerInternalAPIBridge.java | 7 ++ .../datacomponent/PaperDataComponentType.java | 22 ++--- .../data/typed/PaperTypedDataAdapter.java | 2 +- .../io/papermc/paper/util/FloatSupplier.java | 8 ++ .../PaperEnvironmentalAttribute.java | 32 ++++++++ .../PaperEnvironmentalAttributeContext.java | 81 +++++++++++++++++++ .../PaperEnvironmentalAttributeType.java | 8 +- .../paper/world/attribute/package-info.java | 4 + .../org/bukkit/craftbukkit/CraftWorld.java | 11 ++- 17 files changed, 262 insertions(+), 19 deletions(-) create mode 100644 paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java create mode 100644 paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeContext.java create mode 100644 paper-api/src/main/java/io/papermc/paper/world/attribute/package-info.java create mode 100644 paper-server/patches/sources/net/minecraft/world/attribute/WeatherAttributes.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/timeline/AttributeTrackSampler.java.patch create mode 100644 paper-server/src/main/java/io/papermc/paper/util/FloatSupplier.java create mode 100644 paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java create mode 100644 paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeContext.java create mode 100644 paper-server/src/main/java/io/papermc/paper/world/attribute/package-info.java diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java index 609b4b1c7d97..fe4a8c7ff7d2 100644 --- a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java +++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java @@ -3,6 +3,7 @@ import com.destroystokyo.paper.SkinParts; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.world.attribute.EnvironmentalAttributeContext; import io.papermc.paper.world.damagesource.CombatEntry; import io.papermc.paper.world.damagesource.FallLocationType; import java.util.function.Function; @@ -100,4 +101,6 @@ class Holder { Component defaultMannequinDescription(); GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass); + + EnvironmentalAttributeContext.Builder environmentalAttributeContextBuilder(); } diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java new file mode 100644 index 000000000000..e0c632c2ce01 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java @@ -0,0 +1,22 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.math.Position; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface EnvironmentalAttribute { + + T getGlobal(); + + default T getPositioned(Position position) { + return this.getValue(EnvironmentalAttributeContext.builder().position(position).build()); + } + + default T getTimed(long time) { + return this.getValue(EnvironmentalAttributeContext.builder().time(time).build()); + } + + T getValue(EnvironmentalAttributeContext context); + +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeContext.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeContext.java new file mode 100644 index 000000000000..c83dbf910613 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeContext.java @@ -0,0 +1,43 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.InternalAPIBridge; +import io.papermc.paper.math.Position; +import org.bukkit.WeatherType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface EnvironmentalAttributeContext { + + @Nullable Long time(); + + @Nullable Position position(); + + @Nullable Float rainLevel(); + + @Nullable Float thunderLevel(); + + static Builder builder() { + return InternalAPIBridge.get().environmentalAttributeContextBuilder(); + } + + @ApiStatus.NonExtendable + interface Builder { + + Builder time(@Nullable Long time); + + Builder position(@Nullable Position position); + + Builder rainLevel(@Nullable Float rainLevel); + + Builder raining(boolean raining); + + Builder thunderLevel(@Nullable Float thunderLevel); + + Builder thundering(boolean thundering); + + EnvironmentalAttributeContext build(); + + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java index c8f0cf5155fc..18197a02299e 100644 --- a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java @@ -2,9 +2,7 @@ import org.bukkit.Keyed; import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; -@NullMarked @ApiStatus.Experimental @ApiStatus.NonExtendable public interface EnvironmentalAttributeType extends Keyed { diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/package-info.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/package-info.java new file mode 100644 index 000000000000..9f581b167111 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.world.attribute; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index 698f730efe1c..69291c0fe76d 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -11,6 +11,8 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import io.papermc.paper.world.attribute.EnvironmentalAttribute; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; @@ -4592,6 +4594,8 @@ default void setNoTickViewDistance(int viewDistance) { @NotNull public Collection getStructures(int x, int z, @NotNull Structure structure); + @NotNull EnvironmentalAttribute getEnvironmentalAttribute(@NotNull EnvironmentalAttributeType type); + /** * Represents various map environment types that a world may be */ diff --git a/paper-server/patches/sources/net/minecraft/world/attribute/WeatherAttributes.java.patch b/paper-server/patches/sources/net/minecraft/world/attribute/WeatherAttributes.java.patch new file mode 100644 index 000000000000..a7ae3b32275f --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/attribute/WeatherAttributes.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/attribute/WeatherAttributes.java ++++ b/net/minecraft/world/attribute/WeatherAttributes.java +@@ -69,12 +_,12 @@ + return new WeatherAttributes.WeatherAccess() { + @Override + public float rainLevel() { +- return level.getRainLevel(1.0F); ++ return io.papermc.paper.world.attribute.PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.get().rainLevel(() -> level.getRainLevel(1.0F)); // Paper - Environmental Attribute API + } + + @Override + public float thunderLevel() { +- return level.getThunderLevel(1.0F); ++ return io.papermc.paper.world.attribute.PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.get().thunderLevel(() -> level.getThunderLevel(1.0F)); // Paper - Environmental Attribute API + } + }; + } diff --git a/paper-server/patches/sources/net/minecraft/world/timeline/AttributeTrackSampler.java.patch b/paper-server/patches/sources/net/minecraft/world/timeline/AttributeTrackSampler.java.patch new file mode 100644 index 000000000000..60e696bcbb2c --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/timeline/AttributeTrackSampler.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/timeline/AttributeTrackSampler.java ++++ b/net/minecraft/world/timeline/AttributeTrackSampler.java +@@ -32,7 +_,7 @@ + public Value applyTimeBased(Value baseValue, int cacheTickId) { + if (this.cachedArgument == null || cacheTickId != this.cachedTickId) { + this.cachedTickId = cacheTickId; +- this.cachedArgument = this.argumentSampler.sample(this.dayTimeGetter.getAsLong()); ++ this.cachedArgument = this.argumentSampler.sample(io.papermc.paper.world.attribute.PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.get().time(this.dayTimeGetter)); // Paper - Environmental Attribute API + } + + return this.modifier.apply(baseValue, this.cachedArgument); diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java index f7979f188c3d..c20e434cc096 100644 --- a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -6,6 +6,8 @@ import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.datacomponent.item.PaperResolvableProfile; import io.papermc.paper.datacomponent.item.ResolvableProfile; +import io.papermc.paper.world.attribute.EnvironmentalAttributeContext; +import io.papermc.paper.world.attribute.PaperEnvironmentalAttributeContext; import io.papermc.paper.world.damagesource.CombatEntry; import io.papermc.paper.world.damagesource.FallLocationType; import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper; @@ -116,4 +118,9 @@ public Component defaultMannequinDescription() { public GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass) { return CraftGameRule.wrap(rule, fromLegacyToModern, toLegacyFromModern, legacyClass); } + + @Override + public EnvironmentalAttributeContext.Builder environmentalAttributeContextBuilder() { + return new PaperEnvironmentalAttributeContext.PaperBuilder(); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java index 31eed72cbaca..80767b7fd543 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java @@ -90,7 +90,7 @@ import static io.papermc.paper.util.MCUtil.transformUnmodifiable; -public abstract class PaperDataComponentType extends HolderableBase> implements DataComponentType { +public abstract class PaperDataComponentType extends HolderableBase> implements DataComponentType { private static final PaperTypedDataAdapters ADAPTERS = PaperTypedDataAdapters.create( BuiltInRegistries.DATA_COMPONENT_TYPE, @@ -231,9 +231,9 @@ public static Set minecraftToBukkit(final Set adapter; + private final PaperTypedDataAdapter adapter; - private PaperDataComponentType(final Holder> holder, final PaperTypedDataAdapter adapter) { + private PaperDataComponentType(final Holder> holder, final PaperTypedDataAdapter adapter) { super(holder); this.adapter = adapter; } @@ -243,13 +243,13 @@ public boolean isPersistent() { return !this.getHandle().isTransient(); } - public PaperTypedDataAdapter getAdapter() { + public PaperTypedDataAdapter getAdapter() { return this.adapter; } @SuppressWarnings("unchecked") public static DataComponentType of(final Holder holder) { - final PaperTypedDataAdapter< NMS, ?> adapter = PaperDataComponentType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); + final PaperTypedDataAdapter adapter = PaperDataComponentType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); if (adapter == null) { throw new IllegalArgumentException("No adapter found for " + holder); } @@ -262,31 +262,31 @@ public static DataComponentType of(final Holder holder) { } } - public static final class NonValuedImpl extends PaperDataComponentType implements NonValued { + public static final class NonValuedImpl extends PaperDataComponentType implements NonValued { NonValuedImpl( final Holder> holder, - final PaperTypedDataAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } } - public static final class ValuedImpl extends PaperDataComponentType implements Valued { + public static final class ValuedImpl extends PaperDataComponentType implements Valued { ValuedImpl( final Holder> holder, - final PaperTypedDataAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } } - public static final class Unimplemented extends PaperDataComponentType { + public static final class Unimplemented extends PaperDataComponentType { public Unimplemented( final Holder> holder, - final PaperTypedDataAdapter adapter + final PaperTypedDataAdapter adapter ) { super(holder, adapter); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java index eb978dfaa5cd..8bec8480fc30 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/typed/PaperTypedDataAdapter.java @@ -8,7 +8,7 @@ import org.bukkit.craftbukkit.CraftRegistry; import org.jspecify.annotations.Nullable; -public record PaperTypedDataAdapter( +public record PaperTypedDataAdapter( Function apiToVanilla, Function vanillaToApi, @Nullable Codec codec diff --git a/paper-server/src/main/java/io/papermc/paper/util/FloatSupplier.java b/paper-server/src/main/java/io/papermc/paper/util/FloatSupplier.java new file mode 100644 index 000000000000..f2ef588a9720 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/util/FloatSupplier.java @@ -0,0 +1,8 @@ +package io.papermc.paper.util; + +@FunctionalInterface +public interface FloatSupplier { + + float getAsFloat(); + +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java new file mode 100644 index 000000000000..ca2eb9772acc --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java @@ -0,0 +1,32 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; +import io.papermc.paper.util.MCUtil; +import net.minecraft.world.attribute.EnvironmentAttributeSystem; + +public class PaperEnvironmentalAttribute implements EnvironmentalAttribute { + + private final EnvironmentAttributeSystem attributeSystem; + private final PaperEnvironmentalAttributeType type; + private final PaperTypedDataAdapter adapter; + + public PaperEnvironmentalAttribute(final EnvironmentAttributeSystem attributeSystem, final PaperEnvironmentalAttributeType type) { + this.attributeSystem = attributeSystem; + this.type = type; + this.adapter = type.getAdapter(); + } + + @Override + public API getGlobal() { + return this.adapter.fromVanilla(this.attributeSystem.getDimensionValue(this.type.getHandle())); + } + + @Override + public API getValue(final EnvironmentalAttributeContext context) { + PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.set((PaperEnvironmentalAttributeContext) context); + this.attributeSystem.invalidateTickCache(); // Invalidate cache, otherwise it would return the cached value if it was already requested in the same tick + API value = context.position() == null ? this.getGlobal() : this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(context.position()))); + PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.set(PaperEnvironmentalAttributeContext.EMPTY); + return value; + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeContext.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeContext.java new file mode 100644 index 000000000000..f7d9b9fe367b --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeContext.java @@ -0,0 +1,81 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.math.Position; +import io.papermc.paper.util.FloatSupplier; +import java.util.function.LongSupplier; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; + +public record PaperEnvironmentalAttributeContext( + @Nullable Long time, + @Nullable Position position, + @Nullable Float rainLevel, + @Nullable Float thunderLevel +) implements EnvironmentalAttributeContext { + + public static final PaperEnvironmentalAttributeContext EMPTY = new PaperEnvironmentalAttributeContext(null, null, null, null); + public static final ThreadLocal<@NonNull PaperEnvironmentalAttributeContext> CURRENT_CONTEXT = ThreadLocal.withInitial(() -> PaperEnvironmentalAttributeContext.EMPTY); + + public long time(LongSupplier fallbackSupplier) { + return this.time != null ? this.time : fallbackSupplier.getAsLong(); + } + + public float rainLevel(FloatSupplier fallbackSupplier) { + return this.rainLevel != null ? this.rainLevel : fallbackSupplier.getAsFloat(); + } + + public float thunderLevel(FloatSupplier fallbackSupplier) { + return this.thunderLevel != null ? this.thunderLevel : fallbackSupplier.getAsFloat(); + } + + public static class PaperBuilder implements EnvironmentalAttributeContext.Builder { + + private @Nullable Long time; + private @Nullable Position position; + private @Nullable Float rainLevel; + private @Nullable Float thunderLevel; + + @Override + public Builder time(@Nullable final Long time) { + this.time = time; + return this; + } + + @Override + public Builder position(@Nullable final Position position) { + this.position = position; + return this; + } + + @Override + public Builder rainLevel(@Nullable final Float rainLevel) { + this.rainLevel = rainLevel; + return this; + } + + @Override + public Builder raining(final boolean raining) { + return this.rainLevel(raining ? 1.0F : 0.0F); + } + + @Override + public Builder thunderLevel(@Nullable final Float thunderLevel) { + this.thunderLevel = thunderLevel; + return this; + } + + @Override + public Builder thundering(final boolean thundering) { + return this.thunderLevel(thundering ? 1.0F : 0.0F); + } + + @Override + public EnvironmentalAttributeContext build() { + if (this.time == null && this.position == null && this.rainLevel == null && this.thunderLevel == null) { + return PaperEnvironmentalAttributeContext.EMPTY; + } + + return new PaperEnvironmentalAttributeContext(this.time, this.position, this.rainLevel, this.thunderLevel); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java index a0a9f076b338..5b6f87e8bff9 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java @@ -69,9 +69,9 @@ public final class PaperEnvironmentalAttributeType extends HolderableB } ); - private final PaperTypedDataAdapter adapter; + private final PaperTypedDataAdapter adapter; - private PaperEnvironmentalAttributeType(Holder> holder, PaperTypedDataAdapter adapter) { + private PaperEnvironmentalAttributeType(Holder> holder, PaperTypedDataAdapter adapter) { super(holder); this.adapter = adapter; @@ -79,7 +79,7 @@ private PaperEnvironmentalAttributeType(Holder> holder @SuppressWarnings("unchecked") public static EnvironmentalAttributeType of(final Holder holder) { - final PaperTypedDataAdapter adapter = PaperEnvironmentalAttributeType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); + final PaperTypedDataAdapter adapter = PaperEnvironmentalAttributeType.ADAPTERS.getAdapter(holder.unwrapKey().orElseThrow()); if (adapter == null) { throw new IllegalArgumentException("No adapter found for " + holder); } @@ -94,7 +94,7 @@ public API getDefaultValue() { return this.getAdapter().fromVanilla(this.getHandle().defaultValue()); } - public PaperTypedDataAdapter getAdapter() { + public PaperTypedDataAdapter getAdapter() { return this.adapter; } } diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/package-info.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/package-info.java new file mode 100644 index 000000000000..9f581b167111 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.world.attribute; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 5d1afba644e0..e45eebb99566 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -12,6 +12,10 @@ import io.papermc.paper.raytracing.RayTraceTarget; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.world.attribute.EnvironmentalAttribute; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; +import io.papermc.paper.world.attribute.PaperEnvironmentalAttribute; +import io.papermc.paper.world.attribute.PaperEnvironmentalAttributeType; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.nio.file.Path; import java.util.ArrayList; @@ -57,8 +61,8 @@ import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; -import net.minecraft.world.attribute.BedRule; import net.minecraft.util.NullOps; +import net.minecraft.world.attribute.BedRule; import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; @@ -2049,4 +2053,9 @@ public net.kyori.adventure.pointer.Pointers pointers() { return POINTERS_SUPPLIER.view(this); } // Paper end + + @Override + public @NotNull EnvironmentalAttribute getEnvironmentalAttribute(@NotNull final EnvironmentalAttributeType type) { + return new PaperEnvironmentalAttribute<>(this.getHandle().environmentAttributes(), (PaperEnvironmentalAttributeType) type); + } } From c39a37eaa08bef23058d93992b65ae20e8ad588a Mon Sep 17 00:00:00 2001 From: roro1506HD Date: Sun, 30 Nov 2025 23:44:44 +0100 Subject: [PATCH 6/6] feat: added Block#getAttributeValue(..) and optimized positioned lookups --- .../attribute/EnvironmentalAttribute.java | 4 +--- .../src/main/java/org/bukkit/block/Block.java | 3 +++ .../PaperEnvironmentalAttribute.java | 24 ++++++++++++++++++- .../org/bukkit/craftbukkit/CraftWorld.java | 2 +- .../bukkit/craftbukkit/block/CraftBlock.java | 7 ++++++ 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java index e0c632c2ce01..121bb4c11be8 100644 --- a/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java @@ -9,9 +9,7 @@ public interface EnvironmentalAttribute { T getGlobal(); - default T getPositioned(Position position) { - return this.getValue(EnvironmentalAttributeContext.builder().position(position).build()); - } + T getPositioned(Position position); default T getTimed(long time) { return this.getValue(EnvironmentalAttributeContext.builder().time(time).build()); diff --git a/paper-api/src/main/java/org/bukkit/block/Block.java b/paper-api/src/main/java/org/bukkit/block/Block.java index fbee4ab2faae..855e8fc4c87d 100644 --- a/paper-api/src/main/java/org/bukkit/block/Block.java +++ b/paper-api/src/main/java/org/bukkit/block/Block.java @@ -1,5 +1,6 @@ package org.bukkit.block; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import java.util.Collection; import org.bukkit.Chunk; import org.bukkit.FluidCollisionMode; @@ -834,4 +835,6 @@ default float getDestroySpeed(@NotNull ItemStack itemStack, boolean considerEnch * @return {@code true} if the block can suffocate */ boolean isSuffocating(); + + @NotNull T getAttributeValue(@NotNull EnvironmentalAttributeType type); } diff --git a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java index ca2eb9772acc..e96c2abb6ee2 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java @@ -1,7 +1,9 @@ package io.papermc.paper.world.attribute; +import io.papermc.paper.math.Position; import io.papermc.paper.registry.data.typed.PaperTypedDataAdapter; import io.papermc.paper.util.MCUtil; +import net.minecraft.core.BlockPos; import net.minecraft.world.attribute.EnvironmentAttributeSystem; public class PaperEnvironmentalAttribute implements EnvironmentalAttribute { @@ -21,11 +23,31 @@ public API getGlobal() { return this.adapter.fromVanilla(this.attributeSystem.getDimensionValue(this.type.getHandle())); } + @Override + public API getPositioned(final Position position) { + return this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(position))); + } + + public API getPositioned(final BlockPos blockPos) { + return this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), blockPos)); + } + @Override public API getValue(final EnvironmentalAttributeContext context) { + if (context.equals(PaperEnvironmentalAttributeContext.EMPTY)) { + // No field is set, return the global value to prevent invalidating cache + return this.getGlobal(); + } + + Position position = context.position(); + if (position != null && context.time() == null && context.rainLevel() == null && context.thunderLevel() == null) { + // Only position is set, return cached positioned value + return this.getPositioned(position); + } + PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.set((PaperEnvironmentalAttributeContext) context); this.attributeSystem.invalidateTickCache(); // Invalidate cache, otherwise it would return the cached value if it was already requested in the same tick - API value = context.position() == null ? this.getGlobal() : this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(context.position()))); + API value = position == null ? this.getGlobal() : this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(position))); PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.set(PaperEnvironmentalAttributeContext.EMPTY); return value; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index e45eebb99566..d03a2af3d27b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -2055,7 +2055,7 @@ public net.kyori.adventure.pointer.Pointers pointers() { // Paper end @Override - public @NotNull EnvironmentalAttribute getEnvironmentalAttribute(@NotNull final EnvironmentalAttributeType type) { + public @NotNull PaperEnvironmentalAttribute getEnvironmentalAttribute(@NotNull final EnvironmentalAttributeType type) { return new PaperEnvironmentalAttribute<>(this.getHandle().environmentAttributes(), (PaperEnvironmentalAttributeType) type); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java index 31e665388bc6..3909eb06b920 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; @@ -60,6 +61,7 @@ import org.bukkit.util.BoundingBox; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; public class CraftBlock implements Block { private final net.minecraft.world.level.LevelAccessor world; @@ -746,4 +748,9 @@ public void randomTick() { this.getNMS().randomTick(level, this.position, level.random); } // Paper end + + @Override + public @NotNull T getAttributeValue(@NotNull final EnvironmentalAttributeType type) { + return this.getCraftWorld().getEnvironmentalAttribute(type).getPositioned(this.getPosition()); + } }