diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentalAttributeTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentalAttributeTypeKeys.java new file mode 100644 index 000000000000..5dd4bb5ae1a8 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/EnvironmentalAttributeTypeKeys.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 EnvironmentalAttributeTypeKeys { + /** + * {@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 EnvironmentalAttributeTypeKeys() { + } + + 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/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java index 7031a6bc9e60..8abe18b14b46 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.Set; @@ -104,4 +105,6 @@ class Holder { GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass); Set validMannequinPoses(); + + EnvironmentalAttributeContext.Builder environmentalAttributeContextBuilder(); } 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..66d3748a0985 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; @@ -39,6 +40,7 @@ import org.bukkit.map.MapCursor; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; +import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import static io.papermc.paper.registry.RegistryKeyImpl.create; @@ -132,6 +134,12 @@ 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.EnvironmentalAttributeTypeKeys + */ + @ApiStatus.Experimental + RegistryKey> ENVIRONMENT_ATTRIBUTE = create("environment_attribute"); /* ********************** * * Data-driven Registries * diff --git a/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java b/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java index 0b24e1a92bba..fd245c6a55f9 100644 --- a/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java +++ b/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java @@ -6,6 +6,7 @@ @NullMarked public enum MoonPhase { + // todo generate FULL_MOON(0L), WANING_GIBBOUS(1L), LAST_QUARTER(2L), 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..2d7101423c55 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttribute.java @@ -0,0 +1,25 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.math.Position; +import java.util.function.UnaryOperator; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface EnvironmentalAttribute { + + T getGlobal(); + + T getPositioned(Position position); + + default T getTimed(long time) { + return this.getValue(context -> context.time(time)); + } + + default T getValue(UnaryOperator context) { + return this.getValue(context.apply(EnvironmentalAttributeContext.builder()).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..dfd0073d2ff2 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeContext.java @@ -0,0 +1,42 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.InternalAPIBridge; +import io.papermc.paper.math.Position; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.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 new file mode 100644 index 000000000000..18197a02299e --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeType.java @@ -0,0 +1,12 @@ +package io.papermc.paper.world.attribute; + +import org.bukkit.Keyed; +import org.jetbrains.annotations.ApiStatus; + +@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..c3ff32b1858f --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/attribute/EnvironmentalAttributeTypes.java @@ -0,0 +1,121 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.world.MoonPhase; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; +import net.kyori.adventure.util.TriState; +import org.bukkit.Color; +import org.bukkit.Registry; + +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 @KeyPattern.Value String key) { + return (EnvironmentalAttributeType) Registry.ENVIRONMENT_ATTRIBUTE.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } +} 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/Registry.java b/paper-api/src/main/java/org/bukkit/Registry.java index 0859653089ac..3cfa4feae9b6 100644 --- a/paper-api/src/main/java/org/bukkit/Registry.java +++ b/paper-api/src/main/java/org/bukkit/Registry.java @@ -10,6 +10,7 @@ import io.papermc.paper.registry.TypedKey; import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; import java.util.Collection; import java.util.Iterator; import java.util.Locale; @@ -355,6 +356,13 @@ public int size() { * @see GameRule */ Registry> GAME_RULE = registryFor(RegistryKey.GAME_RULE); + /** + * Environmental attribute types. + * + * @see io.papermc.paper.world.attribute.EnvironmentalAttribute + */ + @ApiStatus.Experimental + Registry> ENVIRONMENT_ATTRIBUTE = registryFor(RegistryKey.ENVIRONMENT_ATTRIBUTE); // /** diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index b202bdb84bae..9d14594f4179 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; @@ -4560,6 +4562,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-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-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java index 0a9f1e9a68f7..820637c42921 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 @@ -20,6 +20,8 @@ import io.papermc.paper.registry.data.WolfVariantRegistryEntry; import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry; import io.papermc.paper.registry.data.dialog.DialogRegistryEntry; +import io.papermc.paper.world.attribute.EnvironmentalAttributeType; +import io.papermc.paper.world.attribute.EnvironmentalAttributeTypes; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -39,16 +41,17 @@ 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; import net.minecraft.world.entity.ai.memory.MemoryModuleType; -import net.minecraft.world.entity.animal.feline.CatVariants; import net.minecraft.world.entity.animal.chicken.ChickenVariants; import net.minecraft.world.entity.animal.cow.CowVariants; -import net.minecraft.world.entity.animal.pig.PigVariants; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariants; +import net.minecraft.world.entity.animal.feline.CatVariants; import net.minecraft.world.entity.animal.frog.FrogVariants; +import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariants; +import net.minecraft.world.entity.animal.pig.PigVariants; import net.minecraft.world.entity.animal.wolf.WolfSoundVariants; import net.minecraft.world.entity.animal.wolf.WolfVariants; import net.minecraft.world.entity.decoration.painting.PaintingVariants; @@ -153,7 +156,8 @@ private static RegistryEntry inconsistentEntry(ResourceKey> REGISTRY_CLASS_NAME_BASED_ON_API = Set.of( BlockType.class, - ItemType.class + ItemType.class, + EnvironmentalAttributeType.class ); public static final List> BUILT_IN = List.of( @@ -170,7 +174,8 @@ private static RegistryEntry inconsistentEntry(ResourceKey> DATA_DRIVEN = List.of( 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..199883fed4f0 --- /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 +@@ -67,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 7435a9ebb62f..723142850c45 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; @@ -124,4 +126,9 @@ public GameRule legacyGameRuleBridge(GameRule r public Set validMannequinPoses() { return CraftMannequin.VALID_POSES; } + + @Override + public EnvironmentalAttributeContext.Builder environmentalAttributeContextBuilder() { + return new PaperEnvironmentalAttributeContext.PaperBuilder(); + } } 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/datacomponent/DataComponentAdapter.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java deleted file mode 100644 index 15a96abb2ac7..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.papermc.paper.datacomponent; - -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; - -public record DataComponentAdapter( - Function apiToVanilla, - Function vanillaToApi, - boolean codecValidation -) { - static final Function API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE; - - static final Function API_TO_UNIMPLEMENTED_CONVERTER = $ -> { - throw new UnsupportedOperationException("Cannot convert an API value to an unimplemented type"); - }; - - public boolean isValued() { - return this.apiToVanilla != API_TO_UNIT_CONVERTER; - } - - public boolean isUnimplemented() { - return this.apiToVanilla == API_TO_UNIMPLEMENTED_CONVERTER; - } - - 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 -> { - throw new IllegalArgumentException("Failed to encode data component %s (%s)".formatted(type.unwrapKey().orElseThrow(), error.message())); - }); - } - - return nms; - } - - public API fromVanilla(final NMS value) { - return this.vanillaToApi.apply(value); - } -} 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..48a3f3d9adf3 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.typed.PaperTypedDataAdapter; +import io.papermc.paper.registry.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; -public abstract class PaperDataComponentType extends HolderableBase> implements DataComponentType { +import static io.papermc.paper.util.MCUtil.transformUnmodifiable; - static { - DataComponentAdapters.bootstrap(); - } +public abstract class PaperDataComponentType extends HolderableBase> implements DataComponentType { + + private static final PaperTypedDataAdapters> ADAPTERS = PaperTypedDataAdapters.create( + BuiltInRegistries.DATA_COMPONENT_TYPE, + PaperDataComponentTypeCollector::new, + collector -> { + collector.registerIdentity(DataComponents.MAX_STACK_SIZE); + collector.registerIdentity(DataComponents.MAX_DAMAGE); + collector.registerIdentity(DataComponents.DAMAGE); + collector.registerUntyped(DataComponents.UNBREAKABLE); + collector.register(DataComponents.USE_EFFECTS, PaperUseEffects::new); + collector.registerIdentity(DataComponents.POTION_DURATION_SCALE); + collector.register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.registerIdentity(DataComponents.MINIMUM_ATTACK_CHARGE); + 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); + // registerUntyped(DataComponents.CREATIVE_SLOT_LOCK); + collector.registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE); + 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,50 +243,48 @@ 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()); - if (adapter == null) { - throw new IllegalArgumentException("No adapter found for " + holder); - } + final Holder.Reference> reference = (Holder.Reference>) holder; + final PaperTypedDataAdapter adapter = PaperDataComponentType.ADAPTERS.get(reference.key()); if (adapter.isUnimplemented()) { return new Unimplemented<>((Holder>) holder, adapter); - } else if (adapter.isValued()) { - return new ValuedImpl<>((Holder>) holder, adapter); - } else { + } else if (adapter.isUntyped()) { return new NonValuedImpl<>((Holder>) holder, adapter); + } else { + return new ValuedImpl<>((Holder>) holder, adapter); } } - public static final class NonValuedImpl extends PaperDataComponentType implements NonValued { + public static final class NonValuedImpl extends PaperDataComponentType implements NonValued { NonValuedImpl( final Holder> holder, - final DataComponentAdapter 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 DataComponentAdapter 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 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..824bbea445ac --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentTypeCollector.java @@ -0,0 +1,30 @@ +package io.papermc.paper.datacomponent; + +import io.papermc.paper.registry.typed.AbstractTypedDataCollector; +import io.papermc.paper.registry.typed.PaperTypedDataAdapter; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.resources.ResourceKey; +import org.bukkit.craftbukkit.util.Handleable; + +class PaperDataComponentTypeCollector extends AbstractTypedDataCollector> { + + public PaperDataComponentTypeCollector(final Registry> registry, final Map>, PaperTypedDataAdapter> adapters) { + super(registry, adapters); + } + + public void registerIdentity(final DataComponentType dataComponentType) { + super.registerIdentity(dataComponentType, DataComponentType::codec); + } + + // Not using @Override because of generic types + public void register(final DataComponentType dataComponentType, final Function vanillaToApi, final Function apiToVanilla) { + super.register(dataComponentType, vanillaToApi, apiToVanilla); + } + + public > void register(final DataComponentType dataComponentType, final Function vanillaToApi) { + this.register(dataComponentType, vanillaToApi, Handleable::getHandle); + } +} 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..60d2b36f8262 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.EnvironmentalAttributeTypes; +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(EnvironmentalAttributeTypes.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/registry/typed/AbstractTypedDataCollector.java b/paper-server/src/main/java/io/papermc/paper/registry/typed/AbstractTypedDataCollector.java new file mode 100644 index 000000000000..e446c95e120a --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/typed/AbstractTypedDataCollector.java @@ -0,0 +1,85 @@ +package io.papermc.paper.registry.typed; + +import com.mojang.serialization.Codec; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +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; + + protected AbstractTypedDataCollector( + final Registry registry, + final Map, PaperTypedDataAdapter> adapters + ) { + this.registry = registry; + this.adapters = adapters; + } + + private ResourceKey getKey(final TYPE type) { + return this.registry.getResourceKey(type).orElseThrow(); + } + + @Override + public void registerUntyped(final TYPE type) { + this.registerUntyped(this.getKey(type)); + } + + @Override + public void registerUntyped(final ResourceKey type) { + this.registerInternal(type, PaperTypedDataAdapter.untyped()); + } + + @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 registerIdentity(final Holder.Reference type, final Function> codecGetter) { + this.registerInternal(type.key(), Function.identity(), Function.identity(), codecGetter.apply(type.value())); + } + + @Override + public void registerIdentity(final ResourceKey type, final Function> codecGetter) { + this.registerInternal(type, Function.identity(), Function.identity(), codecGetter.apply(this.registry.getValueOrThrow(type))); + } + + @Override + public void register(final TYPE type, final Function vanillaToApi, final Function apiToVanilla) { + this.register(this.getKey(type), vanillaToApi, apiToVanilla); + } + + @Override + public void register(final ResourceKey type, final Function vanillaToApi, final Function apiToVanilla) { + this.registerInternal(type, vanillaToApi, apiToVanilla, null); + } + + private void registerInternal( + final ResourceKey key, + final Function vanillaToApi, + final Function apiToVanilla, + final @Nullable Codec codec + ) { + this.registerInternal(key, new PaperTypedDataAdapter<>(vanillaToApi, apiToVanilla, codec)); + } + + private void registerInternal( + final ResourceKey key, + final PaperTypedDataAdapter adapter + ) { + if (this.adapters.put(key, adapter) != null) { + throw new IllegalStateException("Duplicate adapter registration for " + key); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapter.java b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapter.java new file mode 100644 index 000000000000..cfc896013b62 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapter.java @@ -0,0 +1,57 @@ +package io.papermc.paper.registry.typed; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import java.util.function.Function; +import net.minecraft.util.NullOps; +import net.minecraft.util.Unit; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jspecify.annotations.Nullable; + +public record PaperTypedDataAdapter( + Function vanillaToApi, + Function apiToVanilla, + @Nullable Codec codec +) { + private static final PaperTypedDataAdapter UNIMPLEMENTED = new PaperTypedDataAdapter<>($ -> { + throw new UnsupportedOperationException("Cannot convert an unimplemented type to an API value"); + }, $ -> { + throw new UnsupportedOperationException("Cannot convert an API value to an unimplemented type"); + }, null); + + private static final PaperTypedDataAdapter UNTYPED = new PaperTypedDataAdapter<>($ -> { + throw new UnsupportedOperationException("Cannot convert the Unit type to an API value"); + }, $ -> Unit.INSTANCE, null); + + @SuppressWarnings("unchecked") + public static PaperTypedDataAdapter unimplemented() { + return (PaperTypedDataAdapter) UNIMPLEMENTED; + } + + @SuppressWarnings("unchecked") + public static PaperTypedDataAdapter untyped() { + return (PaperTypedDataAdapter) UNTYPED; + } + + public boolean isUntyped() { + return this == UNTYPED; + } + + public boolean isUnimplemented() { + return this == UNIMPLEMENTED; + } + + public DataResult toVanilla(final API value) { + final NMS nms = this.apiToVanilla.apply(value); + if (this.codec != null) { + return this.codec.encodeStart(CraftRegistry.getMinecraftRegistry().createSerializationContext(NullOps.INSTANCE), nms) + .error().map(error -> DataResult.error(error::message, nms)).orElseGet(() -> DataResult.success(nms)); + } + + return DataResult.success(nms); + } + + public API fromVanilla(final NMS value) { + return this.vanillaToApi.apply(value); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapters.java b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapters.java new file mode 100644 index 000000000000..a2cadf22f523 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataAdapters.java @@ -0,0 +1,34 @@ +package io.papermc.paper.registry.typed; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.Consumer; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; + +public final class PaperTypedDataAdapters { + + private final Map, PaperTypedDataAdapter> adapters; + + private PaperTypedDataAdapters(final Map, PaperTypedDataAdapter> adapters) { + this.adapters = adapters; + } + + public static > PaperTypedDataAdapters create( + final Registry registry, + final PaperTypedDataCollector.Factory collectorFactory, + final Consumer consumer + ) { + Map, PaperTypedDataAdapter> adapters = new IdentityHashMap<>(); + COLLECTOR collector = collectorFactory.create(registry, adapters); + + consumer.accept(collector); + + return new PaperTypedDataAdapters<>(adapters); + } + + @SuppressWarnings("unchecked") + public > ADAPTER get(final ResourceKey key) { + return (ADAPTER) this.adapters.getOrDefault(key, PaperTypedDataAdapter.unimplemented()); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataCollector.java b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataCollector.java new file mode 100644 index 000000000000..1959b84d7b78 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/typed/PaperTypedDataCollector.java @@ -0,0 +1,40 @@ +package io.papermc.paper.registry.typed; + +import com.mojang.serialization.Codec; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import org.jspecify.annotations.Nullable; + +public interface PaperTypedDataCollector { + + void registerUntyped(TYPE type); + + default void registerUntyped(Holder.Reference type) { + this.registerUntyped(type.key()); + } + + void registerUntyped(ResourceKey type); + + void registerIdentity(TYPE type, Function> codecGetter); + + void registerIdentity(Holder.Reference type, Function> codecGetter); + + void registerIdentity(ResourceKey type, Function> codecGetter); + + void register(TYPE type, Function vanillaToApi, Function apiToVanilla); + + default void register(Holder.Reference type, Function vanillaToApi, Function apiToVanilla) { + this.register(type.key(), vanillaToApi, apiToVanilla); + } + + void register(ResourceKey type, Function vanillaToApi, Function apiToVanilla); + + interface Factory> { + + COLLECTOR create(Registry registry, Map, PaperTypedDataAdapter> adapters); + + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/typed/package-info.java b/paper-server/src/main/java/io/papermc/paper/registry/typed/package-info.java new file mode 100644 index 000000000000..c1a24cf2d68d --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/typed/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.registry.typed; + +import org.jspecify.annotations.NullMarked; 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..b41b1dd7f0ec --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttribute.java @@ -0,0 +1,56 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.math.Position; +import io.papermc.paper.registry.typed.PaperTypedDataAdapter; +import io.papermc.paper.util.MCUtil; +import net.minecraft.core.BlockPos; +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 getPositioned(final Position position) { + return this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(position))); + } + + public API getPositioned(final BlockPos pos) { + return this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), pos)); + } + + @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); + try { + this.attributeSystem.invalidateTickCache(); // Invalidate cache, otherwise it would return the cached value if it was already requested in the same tick + return position == null ? this.getGlobal() : this.adapter.fromVanilla(this.attributeSystem.getValue(this.type.getHandle(), MCUtil.toVec3(position))); + } finally { + PaperEnvironmentalAttributeContext.CURRENT_CONTEXT.set(PaperEnvironmentalAttributeContext.EMPTY); + } + } +} 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..19c86f455539 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeContext.java @@ -0,0 +1,80 @@ +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.jspecify.annotations.Nullable; + +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 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(final @Nullable Long time) { + this.time = time; + return this; + } + + @Override + public Builder position(final @Nullable Position position) { + this.position = position; + return this; + } + + @Override + public Builder rainLevel(final @Nullable 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(final @Nullable 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 new file mode 100644 index 000000000000..e2d6bfe2fec2 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeType.java @@ -0,0 +1,95 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.HolderableBase; +import io.papermc.paper.registry.typed.PaperTypedDataAdapter; +import io.papermc.paper.registry.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); + collector.registerIdentity(EnvironmentAttributes.MUSIC_VOLUME); + + // Gameplay + // collector.register(EnvironmentAttributes.BABY_VILLAGER_ACTIVITY, toApi, toNms); + // collector.register(EnvironmentAttributes.BED_RULE, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.BEES_STAY_IN_HIVE); + collector.registerIdentity(EnvironmentAttributes.CAN_PILLAGER_PATROL_SPAWN); + collector.registerIdentity(EnvironmentAttributes.CAN_START_RAID); + collector.registerIdentity(EnvironmentAttributes.CAT_WAKING_UP_GIFT_CHANCE); + collector.registerIdentity(EnvironmentAttributes.CREAKING_ACTIVE); + collector.register(EnvironmentAttributes.EYEBLOSSOM_OPEN, PaperAdventure::asAdventure, PaperAdventure::asVanilla); + collector.registerIdentity(EnvironmentAttributes.FAST_LAVA); + collector.registerIdentity(EnvironmentAttributes.INCREASED_FIRE_BURNOUT); + collector.registerIdentity(EnvironmentAttributes.MONSTERS_BURN); + collector.registerIdentity(EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS); + collector.registerIdentity(EnvironmentAttributes.PIGLINS_ZOMBIFY); + collector.registerIdentity(EnvironmentAttributes.RESPAWN_ANCHOR_WORKS); + collector.registerIdentity(EnvironmentAttributes.SKY_LIGHT_LEVEL); + collector.registerIdentity(EnvironmentAttributes.SNOW_GOLEM_MELTS); + collector.registerIdentity(EnvironmentAttributes.SURFACE_SLIME_SPAWN_CHANCE); + collector.registerIdentity(EnvironmentAttributes.TURTLE_EGG_HATCH_CHANCE); + // collector.register(EnvironmentAttributes.VILLAGER_ACTIVITY, toApi, toNms); + collector.registerIdentity(EnvironmentAttributes.WATER_EVAPORATES); + + // Visual + // collector.register(EnvironmentAttributes.AMBIENT_PARTICLES, toApi, toNms); + collector.registerIntAsColor(EnvironmentAttributes.CLOUD_COLOR); + collector.registerIdentity(EnvironmentAttributes.CLOUD_FOG_END_DISTANCE); + collector.registerIdentity(EnvironmentAttributes.CLOUD_HEIGHT); + // collector.register(EnvironmentAttributes.DEFAULT_DRIPSTONE_PARTICLE, toApi, toNms); + collector.registerIntAsColor(EnvironmentAttributes.FOG_COLOR); + collector.registerIdentity(EnvironmentAttributes.FOG_END_DISTANCE); + collector.registerIdentity(EnvironmentAttributes.FOG_START_DISTANCE); + collector.registerIdentity(EnvironmentAttributes.MOON_ANGLE); + collector.register(EnvironmentAttributes.MOON_PHASE, moonPhase -> io.papermc.paper.world.MoonPhase.values()[moonPhase.ordinal()], moonPhase -> MoonPhase.values()[moonPhase.ordinal()]); + collector.registerIntAsColor(EnvironmentAttributes.SKY_COLOR); + collector.registerIdentity(EnvironmentAttributes.SKY_FOG_END_DISTANCE); + collector.registerIntAsColor(EnvironmentAttributes.SKY_LIGHT_COLOR); + collector.registerIdentity(EnvironmentAttributes.SKY_LIGHT_FACTOR); + collector.registerIdentity(EnvironmentAttributes.STAR_ANGLE); + collector.registerIdentity(EnvironmentAttributes.STAR_BRIGHTNESS); + collector.registerIdentity(EnvironmentAttributes.SUN_ANGLE); + collector.registerIntAsColor(EnvironmentAttributes.SUNRISE_SUNSET_COLOR); + collector.registerIntAsColor(EnvironmentAttributes.WATER_FOG_COLOR); + collector.registerIdentity(EnvironmentAttributes.WATER_FOG_END_DISTANCE); + collector.registerIdentity(EnvironmentAttributes.WATER_FOG_START_DISTANCE); + } + ); + + 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 Holder.Reference> reference = (Holder.Reference>) holder; + final PaperTypedDataAdapter adapter = PaperEnvironmentalAttributeType.ADAPTERS.get(reference.key()); + return new PaperEnvironmentalAttributeType<>(reference, 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..058179d39490 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/attribute/PaperEnvironmentalAttributeTypeCollector.java @@ -0,0 +1,43 @@ +package io.papermc.paper.world.attribute; + +import io.papermc.paper.registry.typed.AbstractTypedDataCollector; +import io.papermc.paper.registry.typed.PaperTypedDataAdapter; +import java.util.Map; +import java.util.function.Function; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.ARGB; +import net.minecraft.world.attribute.AttributeTypes; +import net.minecraft.world.attribute.EnvironmentAttribute; +import org.bukkit.Color; + +class PaperEnvironmentalAttributeTypeCollector extends AbstractTypedDataCollector> { + + public PaperEnvironmentalAttributeTypeCollector(final Registry> registry, final Map>, PaperTypedDataAdapter> adapters) { + super(registry, adapters); + } + + public void registerIdentity(final EnvironmentAttribute attribute) { + super.registerIdentity(attribute, EnvironmentAttribute::valueCodec); + } + + public void registerIntAsColor(final EnvironmentAttribute attribute) { + if (attribute.type() == AttributeTypes.ARGB_COLOR) { + this.register(attribute, Color::fromARGB, Color::asARGB); + } else if (attribute.type() == AttributeTypes.RGB_COLOR) { + this.register(attribute, color -> Color.fromRGB(color & 0x00FFFFFF), color -> ARGB.opaque(color.asRGB())); + } else { + throw new IllegalArgumentException("Environment attribute value cannot be converted as a color: " + attribute.type()); + } + } + + @Override + public void registerUntyped(final EnvironmentAttribute attribute) { + throw new IllegalStateException("Non-typed adapter is not supported for environmental attribute types! got: " + attribute); // TODO should be restricted by API design not at runtime + } + + // Not using @Override because of generic types + public void register(final EnvironmentAttribute attribute, final Function vanillaToApi, final Function apiToVanilla) { + super.register(attribute, vanillaToApi, apiToVanilla); + } +} 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 ebc65e3338c6..7fc9570f6ee8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -13,6 +13,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; @@ -2038,4 +2042,9 @@ public net.kyori.adventure.pointer.Pointers pointers() { return POINTERS_SUPPLIER.view(this); } // Paper end + + @Override + public PaperEnvironmentalAttribute getEnvironmentalAttribute(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..a94af271dc8c 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; @@ -746,4 +747,9 @@ public void randomTick() { this.getNMS().randomTick(level, this.position, level.random); } // Paper end + + @Override + public T getAttributeValue(final EnvironmentalAttributeType type) { + return this.getCraftWorld().getEnvironmentalAttribute(type).getPositioned(this.position); + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 002682049fe8..77d7ef972f6c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -582,7 +582,9 @@ public void setData(final io.papermc.paper.datacomponent.DataComponentType.NonVa } private void setDataInternal(final io.papermc.paper.datacomponent.PaperDataComponentType type, final A value) { - this.handle.set(type.getHandle(), type.getAdapter().toVanilla(value, type.getHolder())); + this.handle.set(type.getHandle(), type.getAdapter().toVanilla(value).getOrThrow(message -> { + return new IllegalArgumentException("Failed to encode data component %s (%s)".formatted(type.getKey().asString(), message)); + })); } @Override 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) {