diff --git a/build-data/paper.at b/build-data/paper.at index 9c2035671075..2643186a338a 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -625,6 +625,10 @@ public net.minecraft.world.level.dimension.end.EnderDragonFight spawnNewGateway( public net.minecraft.world.level.entity.PersistentEntitySectionManager ensureChunkQueuedForLoad(J)V public net.minecraft.world.level.entity.PersistentEntitySectionManager permanentStorage public net.minecraft.world.level.gamerules.GameRules rules +public net.minecraft.world.level.levelgen.blockpredicates.CombiningPredicate predicates +public net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate blocks +public net.minecraft.world.level.levelgen.blockpredicates.MatchingFluidsPredicate fluids +public net.minecraft.world.level.levelgen.blockpredicates.StateTestingPredicate offset public net.minecraft.world.level.levelgen.material.MaterialRuleContext context public net.minecraft.world.level.levelgen.structure.StructurePiece SHAPE_CHECK_BLOCKS public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement exclusionZone diff --git a/paper-api/.checkstyle/ignored_directories.txt b/paper-api/.checkstyle/ignored_directories.txt index 2d76afe8944c..325ad84d6418 100644 --- a/paper-api/.checkstyle/ignored_directories.txt +++ b/paper-api/.checkstyle/ignored_directories.txt @@ -30,6 +30,7 @@ paper-api/src/main/java/io/papermc/paper/block/ paper-api/src/main/java/io/papermc/paper/block/bed/ paper-api/src/main/java/io/papermc/paper/block/fluid/ paper-api/src/main/java/io/papermc/paper/block/fluid/type/ +paper-api/src/main/java/io/papermc/paper/block/stateprovider/ paper-api/src/main/java/io/papermc/paper/brigadier/ paper-api/src/main/java/io/papermc/paper/chat/ paper-api/src/main/java/io/papermc/paper/command/ @@ -47,6 +48,7 @@ paper-api/src/main/java/io/papermc/paper/datacomponent/item/ paper-api/src/main/java/io/papermc/paper/datacomponent/item/attribute/ paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/ paper-api/src/main/java/io/papermc/paper/datacomponent/item/consumable/ +paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/ paper-api/src/main/java/io/papermc/paper/datapack/ paper-api/src/main/java/io/papermc/paper/dialog/ paper-api/src/main/java/io/papermc/paper/enchantments/ diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTransformerKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTransformerKeys.java new file mode 100644 index 000000000000..cdf0d9f5ac93 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTransformerKeys.java @@ -0,0 +1,61 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#BLOCK_TRANSFORMER}. + * + * @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 BlockTransformerKeys { + /** + * {@code minecraft:axe} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey AXE = create(key("axe")); + + /** + * {@code minecraft:hoe} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey HOE = create(key("hoe")); + + /** + * {@code minecraft:shovel} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SHOVEL = create(key("shovel")); + + private BlockTransformerKeys() { + } + + /** + * Creates a typed key for {@link BlockTransformer} in the registry {@code minecraft:block_transformer}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.BLOCK_TRANSFORMER, key); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/BlockPredicate.java b/paper-api/src/main/java/io/papermc/paper/block/BlockPredicate.java index 955c8257a168..71a3521d1580 100644 --- a/paper-api/src/main/java/io/papermc/paper/block/BlockPredicate.java +++ b/paper-api/src/main/java/io/papermc/paper/block/BlockPredicate.java @@ -1,6 +1,9 @@ package io.papermc.paper.block; +import io.papermc.paper.annotation.MinecraftVersionDependent; import io.papermc.paper.registry.set.RegistryKeySet; +import java.util.List; +import org.bukkit.block.BlockFace; import org.bukkit.block.BlockType; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; @@ -8,17 +11,56 @@ import org.jspecify.annotations.Nullable; @NullMarked +@MinecraftVersionDependent @ApiStatus.NonExtendable public interface BlockPredicate { - static Builder predicate() { + @Contract(value = "_ -> new", pure = true) + static MatchesBlocks matchesBlocks(final @Nullable RegistryKeySet blocks) { + return predicate().blocks(blocks).build(); + } + + @Contract(value = "_, _ -> new", pure = true) + static MatchesDirection matchesDirection(final BlockFace direction, final @Nullable RegistryKeySet blocks) { + record MatchesDirectionPredicateImpl(@Nullable RegistryKeySet blocks, @Nullable BlockFace direction) + implements MatchesDirection { + } + return new MatchesDirectionPredicateImpl(blocks, direction); + } + + @Contract(value = "_ -> new", pure = true) + static AnyOf anyOf(final List predicates) { + return () -> List.copyOf(predicates); + } + + @Contract(value = "_ -> new", pure = true) + static AnyOf anyOf(final BlockPredicate... predicates) { + return anyOf(List.of(predicates)); + } + + @Contract(value = "_ -> new", pure = true) + static AllOf allOf(final List predicates) { + return () -> List.copyOf(predicates); + } + + @Contract(value = "_ -> new", pure = true) + static AllOf allOf(final BlockPredicate... predicates) { + return allOf(List.of(predicates)); + } + + static DirectionalBuilder predicate() { // - record BlockPredicateImpl(@Nullable RegistryKeySet blocks) implements BlockPredicate { + record MatchesBlocksPredicateImpl(@Nullable RegistryKeySet blocks) implements MatchesBlocks { + } + + record MatchesDirectionPredicateImpl(@Nullable RegistryKeySet blocks, @Nullable BlockFace direction) + implements MatchesDirection { } - class BuilderImpl implements Builder { + class BuilderImpl implements DirectionalBuilder { private @Nullable RegistryKeySet blocks; + private @Nullable BlockFace direction; @Override public Builder blocks(final @Nullable RegistryKeySet blocks) { @@ -27,15 +69,43 @@ public Builder blocks(final @Nullable RegistryKeySet blocks) { } @Override - public BlockPredicate build() { - return new BlockPredicateImpl(this.blocks); + public DirectionalBuilder direction(final @Nullable BlockFace direction) { + this.direction = direction; + return this; + } + + @Override + public MatchesBlocks build() { + return this.direction == null + ? new MatchesBlocksPredicateImpl(this.blocks) + : new MatchesDirectionPredicateImpl(this.blocks, this.direction); } } // return new BuilderImpl(); } - @Nullable RegistryKeySet blocks(); + @ApiStatus.NonExtendable + interface MatchesBlocks extends BlockPredicate { + + @Nullable RegistryKeySet blocks(); + } + + @ApiStatus.NonExtendable + interface MatchesDirection extends MatchesBlocks { + + @Override + @Nullable RegistryKeySet blocks(); + + @Nullable BlockFace direction(); + } + + @ApiStatus.NonExtendable + interface DirectionalBuilder extends Builder { + + @Contract(value = "_ -> this", mutates = "this") + DirectionalBuilder direction(@Nullable BlockFace direction); + } @ApiStatus.NonExtendable interface Builder { @@ -43,6 +113,18 @@ interface Builder { @Contract(value = "_ -> this", mutates = "this") Builder blocks(@Nullable RegistryKeySet blocks); - BlockPredicate build(); + MatchesBlocks build(); + } + + @ApiStatus.NonExtendable + interface AnyOf extends BlockPredicate { + + List predicates(); + } + + @ApiStatus.NonExtendable + interface AllOf extends BlockPredicate { + + List predicates(); } } diff --git a/paper-api/src/main/java/io/papermc/paper/block/fluid/FluidPredicate.java b/paper-api/src/main/java/io/papermc/paper/block/fluid/FluidPredicate.java new file mode 100644 index 000000000000..132a7320c71a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/fluid/FluidPredicate.java @@ -0,0 +1,40 @@ +package io.papermc.paper.block.fluid; + +import io.papermc.paper.annotation.MinecraftVersionDependent; +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.registry.set.RegistryKeySet; +import org.bukkit.Fluid; +import org.bukkit.block.BlockFace; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +@MinecraftVersionDependent +@ApiStatus.NonExtendable +public interface FluidPredicate extends BlockPredicate { + + static MatchesBlocks matchesFluids(final RegistryKeySet fluids) { + record MatchesBlocksPredicateImpl(RegistryKeySet fluids) implements MatchesBlocks { + } + return new MatchesBlocksPredicateImpl(fluids); + } + + static MatchesDirection matchesDirection(final BlockFace direction, final RegistryKeySet fluids) { + record MatchesDirectionPredicateImpl(RegistryKeySet fluids, BlockFace direction) + implements MatchesDirection { + } + return new MatchesDirectionPredicateImpl(fluids, direction); + } + + @ApiStatus.NonExtendable + interface MatchesBlocks extends FluidPredicate { + + RegistryKeySet fluids(); + } + + @ApiStatus.NonExtendable + interface MatchesDirection extends MatchesBlocks { + + BlockFace direction(); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/BlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/BlockStateProvider.java new file mode 100644 index 000000000000..b7d1fdf91cb4 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/BlockStateProvider.java @@ -0,0 +1,130 @@ +package io.papermc.paper.block.stateprovider; + +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.registry.set.RegistryKeySet; +import java.util.List; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.Nullable; + +@ApiStatus.NonExtendable +public interface BlockStateProvider { + + /** + * Creates a simple provider that always returns the default state of the given block type. + * + * @param blockType block type to provide + * @return a simple block state provider + */ + @Contract(value = "_ -> new", pure = true) + static SimpleBlockStateProvider simple(final BlockType blockType) { + // + record SimpleProviderImpl(BlockType blockType) implements SimpleBlockStateProvider { + } + // + return new SimpleProviderImpl(blockType); + } + + /** + * Creates a provider that copies properties from the current block into the given target block type. + * + * @param blockType target block type + * @return a copy-properties block state provider + */ + @Contract(value = "_ -> new", pure = true) + static CopyPropertiesBlockStateProvider copyPropertiesFrom(final BlockType blockType) { + // + record CopyPropertiesProviderImpl(BlockType blockType) implements CopyPropertiesBlockStateProvider { + } + // + return new CopyPropertiesProviderImpl(blockType); + } + + /** + * Creates a provider that chooses a random block from the provided set. + * + * @param blocks candidate blocks + * @return a random block state provider + */ + @Contract(value = "_ -> new", pure = true) + static RandomBlockStateProvider randomBlock(final RegistryKeySet blocks) { + // + record RandomBlockProviderImpl(RegistryKeySet blocks) implements RandomBlockStateProvider { + } + // + return new RandomBlockProviderImpl(blocks); + } + + /** + * Creates a provider that applies a random rotation to the output of another provider. + * + * @param stateProvider source provider + * @return a rotated block state provider + */ + @Contract(value = "_ -> new", pure = true) + static RotatedBlockStateProvider rotated(final BlockStateProvider stateProvider) { + return rotated(stateProvider, null); + } + + /** + * Creates a provider that rotates the output of another provider with an optional forced direction. + * + * @param stateProvider source provider + * @param direction direction to force, or null for random direction + * @return a rotated block state provider + */ + @Contract(value = "_, _ -> new", pure = true) + static RotatedBlockStateProvider rotated(final BlockStateProvider stateProvider, final @Nullable BlockFace direction) { + // + record RotatedBlockProviderImpl(BlockStateProvider stateProvider, @Nullable BlockFace direction) implements RotatedBlockStateProvider { + } + // + return new RotatedBlockProviderImpl(stateProvider, direction); + } + + /** + * Creates a rule-based provider using the given rules and no fallback provider. + * + * @param rules rule list + * @return a rule-based block state provider + */ + @Contract(value = "_ -> new", pure = true) + static RuleBasedBlockStateProvider ruleBased(final List rules) { + return ruleBased(null, rules); + } + + /** + * Creates a rule-based provider using the given fallback and rules. + * + * @param fallback fallback provider when no rule matches + * @param rules rule list + * @return a rule-based block state provider + */ + @Contract(value = "_, _ -> new", pure = true) + static RuleBasedBlockStateProvider ruleBased(final @Nullable BlockStateProvider fallback, final List rules) { + // + record RuleBasedProviderImpl(@Nullable BlockStateProvider fallback, @Unmodifiable List rules) + implements RuleBasedBlockStateProvider { + RuleBasedProviderImpl { + rules = List.copyOf(rules); + } + } + // + return new RuleBasedProviderImpl(fallback, rules); + } + + /** + * Creates a rule used by {@link #ruleBased(List)} and {@link #ruleBased(BlockStateProvider, List)}. + * + * @param ifTrue predicate that must match + * @param thenProvide provider to use when the predicate matches + * @return an immutable rule + */ + @Contract(value = "_, _ -> new", pure = true) + static RuleBasedBlockStateProvider.Rule rule(final BlockPredicate ifTrue, final BlockStateProvider thenProvide) { + return new RuleBasedBlockStateProvider.Rule(ifTrue, thenProvide); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/CopyPropertiesBlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/CopyPropertiesBlockStateProvider.java new file mode 100644 index 000000000000..97d7f2b6ae84 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/CopyPropertiesBlockStateProvider.java @@ -0,0 +1,17 @@ +package io.papermc.paper.block.stateprovider; + +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +@ApiStatus.NonExtendable +public interface CopyPropertiesBlockStateProvider extends BlockStateProvider { + + /** + * Target block type that receives the copied properties. + * + * @return target block type + */ + @Contract(pure = true) + BlockType blockType(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RandomBlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RandomBlockStateProvider.java new file mode 100644 index 000000000000..656139f34f4b --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RandomBlockStateProvider.java @@ -0,0 +1,18 @@ +package io.papermc.paper.block.stateprovider; + +import io.papermc.paper.registry.set.RegistryKeySet; +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +@ApiStatus.NonExtendable +public interface RandomBlockStateProvider extends BlockStateProvider { + + /** + * Candidate block types used by this random provider. + * + * @return candidate blocks + */ + @Contract(pure = true) + RegistryKeySet blocks(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RotatedBlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RotatedBlockStateProvider.java new file mode 100644 index 000000000000..74bbc5918cd9 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RotatedBlockStateProvider.java @@ -0,0 +1,26 @@ +package io.papermc.paper.block.stateprovider; + +import org.bukkit.block.BlockFace; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.Nullable; + +@ApiStatus.NonExtendable +public interface RotatedBlockStateProvider extends BlockStateProvider { + + /** + * Source provider whose output is rotated. + * + * @return source provider + */ + @Contract(pure = true) + BlockStateProvider stateProvider(); + + /** + * Forced direction for rotation, or null to pick one at random. + * + * @return forced direction, or null + */ + @Contract(pure = true) + @Nullable BlockFace direction(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RuleBasedBlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RuleBasedBlockStateProvider.java new file mode 100644 index 000000000000..d8d16828570a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/RuleBasedBlockStateProvider.java @@ -0,0 +1,31 @@ +package io.papermc.paper.block.stateprovider; + +import io.papermc.paper.block.BlockPredicate; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.Nullable; + +@ApiStatus.NonExtendable +public interface RuleBasedBlockStateProvider extends BlockStateProvider { + + /** + * Fallback provider used when no rule matches, or null if absent. + * + * @return fallback provider or null + */ + @Contract(pure = true) + @Nullable BlockStateProvider fallback(); + + /** + * Ordered rules evaluated from first to last. + * + * @return immutable list of rules + */ + @Contract(pure = true) + @Unmodifiable List rules(); + + record Rule(BlockPredicate ifTrue, BlockStateProvider thenProvide) { + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/SimpleBlockStateProvider.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/SimpleBlockStateProvider.java new file mode 100644 index 000000000000..5b61dfeb3940 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/SimpleBlockStateProvider.java @@ -0,0 +1,17 @@ +package io.papermc.paper.block.stateprovider; + +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +@ApiStatus.NonExtendable +public interface SimpleBlockStateProvider extends BlockStateProvider { + + /** + * Target block type whose default state will be provided. + * + * @return target block type + */ + @Contract(pure = true) + BlockType blockType(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/block/stateprovider/package-info.java b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/package-info.java new file mode 100644 index 000000000000..17e071b39aeb --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/block/stateprovider/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains classes that provide block states for various blocks in Minecraft. + */ +@NullMarked +package io.papermc.paper.block.stateprovider; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java index 6b0c37203bb6..1d8d4ad6bd4c 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java @@ -49,6 +49,7 @@ import io.papermc.paper.datacomponent.item.Weapon; import io.papermc.paper.datacomponent.item.WritableBookContent; import io.papermc.paper.datacomponent.item.WrittenBookContent; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; import io.papermc.paper.item.MapPostProcessing; import io.papermc.paper.registry.set.RegistryKeySet; import java.util.List; @@ -215,6 +216,10 @@ public final class DataComponentTypes { * Controls the behavior of the item as a tool. */ public static final DataComponentType.Valued TOOL = valued("tool"); + /** + * Controls how an item transforms blocks when used on them. + */ + public static final DataComponentType.Valued BLOCK_TRANSFORMER = valued("block_transformer"); public static final DataComponentType.Valued WEAPON = valued("weapon"); public static final DataComponentType.Valued ATTACK_RANGE = valued("attack_range"); public static final DataComponentType.Valued ENCHANTABLE = valued("enchantable"); diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformData.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformData.java new file mode 100644 index 000000000000..08aea22531a0 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformData.java @@ -0,0 +1,92 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.block.stateprovider.BlockStateProvider; +import io.papermc.paper.datacomponent.BuildableDataComponent; +import io.papermc.paper.datacomponent.DataComponentBuilder; +import java.util.List; +import net.kyori.adventure.key.Key; +import org.bukkit.block.BlockFace; +import org.bukkit.loot.LootTable; +import org.checkerframework.checker.index.qual.NonNegative; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.Nullable; + +@ApiStatus.NonExtendable +public interface BlockTransformData extends BuildableDataComponent { + + @Contract(value = "_ -> new", pure = true) + static BlockTransformData.Builder blockTransformData(final BlockStateProvider blockStateProvider) { + return BlockTransformTypesBridge.bridge().blockTransformData(blockStateProvider); + } + + @Contract(value = "_, _ -> new", pure = true) + static BlockTransformData.Builder blockTransformData(final BlockPredicate predicate, final BlockStateProvider blockStateProvider) { + return BlockTransformTypesBridge.bridge().blockTransformData(predicate, blockStateProvider); + } + + @Contract(pure = true) + BlockStateProvider blockStateProvider(); + + @Contract(pure = true) + @Nullable Key sound(); + + @Contract(pure = true) + TransformParticle particle(); + + @Contract(pure = true) + @Unmodifiable List disallowedFaces(); + + @Nullable LootTable lootTable(); + + @Contract(pure = true) + DropStrategy dropStrategy(); + + @Contract(pure = true) + boolean updateFromNeighbors(); + + @Contract(pure = true) + TransformType transformType(); + + @Contract(pure = true) + boolean consumeOnUse(); + + @Contract(pure = true) + @NonNegative int itemDamagePerUse(); + + @ApiStatus.NonExtendable + interface Builder extends DataComponentBuilder { + + @Contract(value = "_ -> this", mutates = "this") + Builder sound(@Nullable Key sound); + + @Contract(value = "_ -> this", mutates = "this") + Builder particle(TransformParticle particle); + + @Contract(value = "_ -> this", mutates = "this") + Builder disallowedFaces(List disallowedFaces); + + @Contract(value = "_ -> this", mutates = "this") + Builder addDisallowedFace(BlockFace disallowedFace); + + @Contract(value = "_ -> this", mutates = "this") + Builder lootTable(@Nullable LootTable lootTable); + + @Contract(value = "_ -> this", mutates = "this") + Builder dropStrategy(DropStrategy dropStrategy); + + @Contract(value = "_ -> this", mutates = "this") + Builder updateFromNeighbors(boolean updateFromNeighbors); + + @Contract(value = "_ -> this", mutates = "this") + Builder transformType(TransformType transformType); + + @Contract(value = "_ -> this", mutates = "this") + Builder consumeOnUse(boolean consumeOnUse); + + @Contract(value = "_ -> this", mutates = "this") + Builder itemDamagePerUse(@NonNegative int itemDamagePerUse); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridge.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridge.java new file mode 100644 index 000000000000..72baf562f14c --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridge.java @@ -0,0 +1,22 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.block.stateprovider.BlockStateProvider; +import java.util.Optional; +import java.util.ServiceLoader; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +interface BlockTransformTypesBridge { + + Optional BRIDGE = ServiceLoader.load(BlockTransformTypesBridge.class, BlockTransformTypesBridge.class.getClassLoader()).findFirst(); + + static BlockTransformTypesBridge bridge() { + return BRIDGE.orElseThrow(); + } + + BlockTransformData.Builder blockTransformData(BlockStateProvider blockStateProvider); + + BlockTransformData.Builder blockTransformData(BlockPredicate predicate, BlockStateProvider blockStateProvider); + +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformer.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformer.java new file mode 100644 index 000000000000..a9ec25915d6f --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformer.java @@ -0,0 +1,14 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import java.util.List; +import org.bukkit.Keyed; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; + +@ApiStatus.NonExtendable +public interface BlockTransformer extends Keyed { + + @Contract(pure = true) + @Unmodifiable List transforms(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformers.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformers.java new file mode 100644 index 000000000000..26517192f0a4 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformers.java @@ -0,0 +1,25 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; + +/** + * All the vanilla block transformers. + */ +public final class BlockTransformers { + + // Start generate - BlockTransformer + public static final BlockTransformer AXE = getBlockTransformer("axe"); + + public static final BlockTransformer HOE = getBlockTransformer("hoe"); + + public static final BlockTransformer SHOVEL = getBlockTransformer("shovel"); + // End generate - BlockTransformer + + private static BlockTransformer getBlockTransformer(final @KeyPattern.Value String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.BLOCK_TRANSFORMER).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/DropStrategy.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/DropStrategy.java new file mode 100644 index 000000000000..5ff6d50d5e59 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/DropStrategy.java @@ -0,0 +1,10 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +public enum DropStrategy { + + // Start generate - DropStrategy + CLICKED_FACE, + FROM_MIDDLE; + // End generate - DropStrategy + +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformParticle.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformParticle.java new file mode 100644 index 000000000000..1c7310bb5f03 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformParticle.java @@ -0,0 +1,12 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +public enum TransformParticle { + + // Start generate - TransformParticle + NONE, + SCRAPE, + WAX_ON, + WAX_OFF; + // End generate - TransformParticle + +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformType.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformType.java new file mode 100644 index 000000000000..201aa18b7f6e --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/TransformType.java @@ -0,0 +1,10 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +public enum TransformType { + + // Start generate - TransformType + SINGLE_BLOCK, + COPPER_CHEST; + // End generate - TransformType + +} diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java new file mode 100644 index 000000000000..78553e87e249 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java @@ -0,0 +1,6 @@ +@NullMarked +@MinecraftVersionDependent +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.annotation.MinecraftVersionDependent; +import org.jspecify.annotations.NullMarked; 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 832a93327397..3821036b3800 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 @@ -2,6 +2,7 @@ import io.papermc.paper.block.pot.PotPatternType; import io.papermc.paper.datacomponent.DataComponentType; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.entity.poi.PoiType; import io.papermc.paper.registry.tag.TagKey; @@ -269,6 +270,10 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.DialogKeys */ RegistryKey DIALOG = create("dialog"); + /** + * Data-driven registry for block transformers. + */ + RegistryKey BLOCK_TRANSFORMER = create("block_transformer"); /* ******************* * diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/BlockTransformerRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/BlockTransformerRegistryEntry.java new file mode 100644 index 000000000000..f765d5446f0a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/BlockTransformerRegistryEntry.java @@ -0,0 +1,54 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformData; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; +import io.papermc.paper.registry.RegistryBuilder; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; + +/** + * A data-centric version-specific registry entry for the {@link BlockTransformer} type. + */ +@ApiStatus.NonExtendable +public interface BlockTransformerRegistryEntry { + + /** + * Gets the ordered transforms for this block transformer. + * + * @return the transforms + */ + @Contract(pure = true) + @Unmodifiable List transforms(); + + /** + * A mutable builder for the {@link BlockTransformerRegistryEntry} plugins may change in applicable registry events. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #addTransform(BlockTransformData)} or {@link #addTransforms(List)}
  • + *
+ */ + @ApiStatus.NonExtendable + interface Builder extends BlockTransformerRegistryEntry, RegistryBuilder { + + /** + * Adds a transform to this block transformer. + * + * @param transform transform to add + * @return this builder instance + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addTransform(BlockTransformData transform); + + /** + * Adds transforms to this block transformer. + * + * @param transforms transforms to add + * @return this builder instance + */ + @Contract(value = "_ -> this", mutates = "this") + Builder addTransforms(List transforms); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index ed59510b3f5d..867d1c2c0a2c 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -1,9 +1,11 @@ package io.papermc.paper.registry.event; import io.papermc.paper.block.pot.PotPatternType; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.registry.data.BannerPatternRegistryEntry; +import io.papermc.paper.registry.data.BlockTransformerRegistryEntry; import io.papermc.paper.registry.data.CatTypeRegistryEntry; import io.papermc.paper.registry.data.ChickenVariantRegistryEntry; import io.papermc.paper.registry.data.CowVariantRegistryEntry; @@ -68,6 +70,7 @@ public final class RegistryEvents { public static final RegistryEventProvider ZOMBIE_NAUTILUS_VARIANT = create(RegistryKey.ZOMBIE_NAUTILUS_VARIANT); public static final RegistryEventProvider SULFUR_CUBE_ARCHETYPE = create(RegistryKey.SULFUR_CUBE_ARCHETYPE); public static final RegistryEventProvider DIALOG = create(RegistryKey.DIALOG); + public static final RegistryEventProvider BLOCK_TRANSFORMER = create(RegistryKey.BLOCK_TRANSFORMER); // End generate - RegistryEvents private RegistryEvents() { diff --git a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java index 88adaaba7614..fd7e876486be 100644 --- a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java +++ b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java @@ -24,6 +24,10 @@ import io.papermc.generator.types.goal.MobGoalNames; import io.papermc.generator.utils.Formatting; import io.papermc.paper.datacomponent.item.SwingAnimation; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformers; +import io.papermc.paper.datacomponent.item.blocktransformer.DropStrategy; +import io.papermc.paper.datacomponent.item.blocktransformer.TransformParticle; +import io.papermc.paper.datacomponent.item.blocktransformer.TransformType; import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.entity.RemovalReason; @@ -180,6 +184,9 @@ protected EnumValue.Builder rewriteEnumValue(Rarity rarity) { ); } }) + .register("DropStrategy", DropStrategy.class, new EnumCloneRewriter<>(net.minecraft.core.component.BlockTransformer.DropStrategy.class)) + .register("TransformParticle", TransformParticle.class, new EnumCloneRewriter<>(net.minecraft.core.component.BlockTransformer.TransformParticle.class)) + .register("TransformType", TransformType.class, new EnumCloneRewriter<>(net.minecraft.core.component.BlockTransformer.TransformType.class)) .register(Material.class, composite( holder("Blocks", new MaterialRewriter.Blocks()), //holder("Material#isTransparent", MaterialRewriter.IsTransparent()), @@ -249,7 +256,8 @@ protected String rewriteFieldType(Holder.Reference(Registries.BLOCK_TRANSFORMER, "getBlockTransformer")); RegistryBootstrapper.bootstrapApi(sourceSet); } 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 f4168a3f884d..d6288af94670 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 @@ -4,9 +4,11 @@ import io.papermc.paper.block.pot.PotPatternType; import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.entity.poi.PoiTypes; import io.papermc.paper.registry.data.BannerPatternRegistryEntry; +import io.papermc.paper.registry.data.BlockTransformerRegistryEntry; import io.papermc.paper.registry.data.CatTypeRegistryEntry; import io.papermc.paper.registry.data.ChickenVariantRegistryEntry; import io.papermc.paper.registry.data.CowVariantRegistryEntry; @@ -69,6 +71,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.JukeboxSongs; import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.item.component.BlockTransformers; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.item.equipment.trim.TrimMaterials; import net.minecraft.world.item.equipment.trim.TrimPatterns; @@ -213,7 +216,8 @@ private static RegistryEntry inconsistentEntry(ResourceKey> API_ONLY = List.of( diff --git a/paper-server/src/main/java/io/papermc/paper/block/PaperBlockPredicate.java b/paper-server/src/main/java/io/papermc/paper/block/PaperBlockPredicate.java new file mode 100644 index 000000000000..05ba534b68e9 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/block/PaperBlockPredicate.java @@ -0,0 +1,122 @@ +package io.papermc.paper.block; + +import io.papermc.paper.block.fluid.FluidPredicate; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.set.RegistryKeySet; +import io.papermc.paper.registry.set.RegistrySet; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; +import org.bukkit.Registry; +import org.bukkit.block.BlockType; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockType; +import org.bukkit.craftbukkit.CraftFluid; +import org.jspecify.annotations.Nullable; + +public final class PaperBlockPredicate { + + private PaperBlockPredicate() { + } + + public static net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate toVanilla(final @Nullable BlockPredicate predicate) { + switch (predicate) { + case null -> { + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.alwaysTrue(); + } + case final FluidPredicate.MatchesDirection fluidMatchesDirection -> { + final List fluids = fluidMatchesDirection.fluids().values().stream() + .map(TypedKey::key) + .map(Registry.FLUID::get) + .filter(java.util.Objects::nonNull) + .map(CraftFluid::bukkitToMinecraft) + .toList(); + final Direction direction = Direction.valueOf(fluidMatchesDirection.direction().name()); + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.matchesFluids( + direction.getStep(), + fluids + ); + } + case final FluidPredicate.MatchesBlocks fluidMatchesBlocks -> { + final List fluids = fluidMatchesBlocks.fluids().values().stream() + .map(TypedKey::key) + .map(Registry.FLUID::get) + .filter(java.util.Objects::nonNull) + .map(CraftFluid::bukkitToMinecraft) + .toList(); + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.matchesFluids(Vec3i.ZERO, fluids); + } + case final BlockPredicate.AnyOf anyOf -> { + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.anyOf( + anyOf.predicates().stream().map(PaperBlockPredicate::toVanilla).toList() + ); + } + case final BlockPredicate.AllOf allOf -> { + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.allOf( + allOf.predicates().stream().map(PaperBlockPredicate::toVanilla).toList() + ); + } + default -> { + final List predicates = new ArrayList<>(); + if (predicate instanceof final BlockPredicate.MatchesBlocks matchesBlocks) { + final RegistryKeySet blocksKeySet = matchesBlocks.blocks(); + final List blocks = blocksKeySet.values().stream() + .map(PaperRegistries::toNms) + .map(key -> BuiltInRegistries.BLOCK.getValue(key.identifier())) + .toList(); + final Block[] array = blocks.toArray(Block[]::new); + predicates.add(net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.matchesBlocks(array)); + } + + if (predicates.isEmpty()) { + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.alwaysTrue(); + } + + return net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.allOf(predicates); + } + } + } + + public static BlockPredicate toApi(final net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate predicate) { + if (predicate == net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.alwaysTrue()) { + return BlockPredicate.predicate().build(); + } + if (predicate instanceof final net.minecraft.world.level.levelgen.blockpredicates.AnyOfPredicate anyOf) { + return BlockPredicate.anyOf(anyOf.predicates.stream().map(PaperBlockPredicate::toApi).toList()); + } + if (predicate instanceof final net.minecraft.world.level.levelgen.blockpredicates.AllOfPredicate allOf) { + return BlockPredicate.allOf(allOf.predicates.stream().map(PaperBlockPredicate::toApi).toList()); + } + if (predicate instanceof final net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate matchingBlocks) { + final RegistryKeySet blockSet = RegistrySet.keySetFromValues( + RegistryKey.BLOCK, + matchingBlocks.blocks.stream().map(holder -> CraftBlockType.minecraftToBukkitNew(holder.value())).toList() + ); + final Vec3i offset = matchingBlocks.offset; + final Direction direction = Direction.getNearest(offset, null); + return direction == null + ? BlockPredicate.matchesBlocks(blockSet) + : BlockPredicate.matchesDirection(CraftBlock.notchToBlockFace(direction), blockSet); + } + if (predicate instanceof final net.minecraft.world.level.levelgen.blockpredicates.MatchingFluidsPredicate matchingFluids) { + final java.util.List> fluidKeys = matchingFluids.fluids.stream() + .map(holder -> PaperRegistries.fromNms(holder.unwrapKey().orElseThrow())) + .map(key -> TypedKey.create(RegistryKey.FLUID, key.key())) + .toList(); + final var fluidSet = RegistrySet.keySet(RegistryKey.FLUID, fluidKeys); + final Vec3i offset = matchingFluids.offset; + final Direction direction = Direction.getNearest(offset, null); + return direction == null + ? io.papermc.paper.block.fluid.FluidPredicate.matchesFluids(fluidSet) + : io.papermc.paper.block.fluid.FluidPredicate.matchesDirection(CraftBlock.notchToBlockFace(direction), fluidSet); + } + + throw new UnsupportedOperationException("Unsupported block predicate type: " + predicate.getClass().getSimpleName()); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/block/stateprovider/PaperBlockStateProvider.java b/paper-server/src/main/java/io/papermc/paper/block/stateprovider/PaperBlockStateProvider.java new file mode 100644 index 000000000000..13e671cb3f23 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/block/stateprovider/PaperBlockStateProvider.java @@ -0,0 +1,109 @@ +package io.papermc.paper.block.stateprovider; + +import io.papermc.paper.block.PaperBlockPredicate; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.set.PaperRegistrySets; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.levelgen.feature.stateproviders.CopyPropertiesProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.RandomBlockProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.RotatedBlockProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.RuleBasedStateProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.SimpleStateProvider; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockType; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; + +public final class PaperBlockStateProvider { + + private PaperBlockStateProvider() { + } + + public static net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider toVanilla(final BlockStateProvider provider) { + switch (provider) { + case final SimpleBlockStateProvider simple -> { + return new SimpleStateProvider(CraftBlockType.bukkitToMinecraftNew(simple.blockType()).defaultBlockState()); + } + case final CopyPropertiesBlockStateProvider copyProperties -> { + return new CopyPropertiesProvider( + CraftBlockType.bukkitToMinecraftNew(copyProperties.blockType()) + ); + } + case final RandomBlockStateProvider randomBlock -> { + final List> blocks = new ArrayList<>(); + for (final var key : randomBlock.blocks().values()) { + final Block block = BuiltInRegistries.BLOCK.getValue(PaperRegistries.toNms(key).identifier()); + blocks.add(BuiltInRegistries.BLOCK.wrapAsHolder(block)); + } + return new RandomBlockProvider(HolderSet.direct(blocks)); + } + case final RotatedBlockStateProvider rotated -> { + final net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider source = toVanilla(rotated.stateProvider()); + final Optional direction = Optional.ofNullable(rotated.direction()) + .map(face -> asArgument(CraftBlock.blockFaceToNotch(face), "direction")); + return new RotatedBlockProvider(Holder.direct(source), direction); + } + case final RuleBasedBlockStateProvider ruleBased -> { + Holder holderFallback = null; + if (ruleBased.fallback() != null) { + holderFallback = Holder.direct(toVanilla(ruleBased.fallback())); + } + final List rules = ruleBased.rules().stream() + .map(rule -> new RuleBasedStateProvider.Rule( + PaperBlockPredicate.toVanilla(rule.ifTrue()), + Holder.direct(toVanilla(rule.thenProvide())) + )) + .toList(); + return new RuleBasedStateProvider(holderFallback, rules); + } + default -> { + throw new UnsupportedOperationException("Unsupported block state provider type: " + provider.getClass().getSimpleName()); + } + } + } + + public static BlockStateProvider toApi(final net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider provider) { + return switch (provider) { + case SimpleStateProvider(net.minecraft.world.level.block.state.BlockState state) -> + BlockStateProvider.simple(CraftBlockType.minecraftToBukkitNew(state.getBlock())); + case CopyPropertiesProvider( + Holder source + ) when source.value() instanceof SimpleStateProvider( + net.minecraft.world.level.block.state.BlockState sourceState + ) -> BlockStateProvider.copyPropertiesFrom(CraftBlockType.minecraftToBukkitNew(sourceState.getBlock())); + case RandomBlockProvider(HolderSet blocks) -> + BlockStateProvider.randomBlock(PaperRegistrySets.convertToApi(RegistryKey.BLOCK, blocks)); + case RotatedBlockProvider( + Holder state, + Optional direction + ) -> BlockStateProvider.rotated( + toApi(state.value()), + direction.map(CraftBlock::notchToBlockFace).orElse(null) + ); + case RuleBasedStateProvider( + Holder fallback, + List rules + ) -> BlockStateProvider.ruleBased( + fallback == null ? null : toApi(fallback.value()), + rules.stream() + .map(rule -> BlockStateProvider.rule( + rule.ifTrue() == net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate.alwaysTrue() + ? io.papermc.paper.block.BlockPredicate.predicate().build() + : PaperBlockPredicate.toApi(rule.ifTrue()), + toApi(rule.then().value()) + )) + .toList() + ); + default -> + throw new UnsupportedOperationException("Unsupported block state provider type: " + provider.getClass().getSimpleName()); + }; + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/block/stateprovider/package-info.java b/paper-server/src/main/java/io/papermc/paper/block/stateprovider/package-info.java new file mode 100644 index 000000000000..f9b604448f25 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/block/stateprovider/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.block.stateprovider; + +import org.jspecify.annotations.NullMarked; 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 index 5e4c51049e03..ae8ede87a3ef 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java @@ -49,6 +49,7 @@ 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.datacomponent.item.blocktransformer.PaperBlockTransformer; import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; @@ -189,7 +190,7 @@ public static void bootstrap() { register(DataComponents.ATTACK_ANIMATION, PaperSwingAnimation::new); register(DataComponents.INTERACT_ANIMATION, PaperSwingAnimation::new); // registerIdentity(DataComponents.ADDITIONAL_TRADE_COST); - // block transformer + register(DataComponents.BLOCK_TRANSFORMER, PaperBlockTransformer::minecraftHolderToBukkit, PaperBlockTransformer::bukkitToMinecraftHolder); register(DataComponents.VILLAGER_FOOD, PaperVillagerFood::new); register(DataComponents.MOB_VISIBILITY, PaperMobVisibility::new); register(DataComponents.VILLAGER_VARIANT, CraftVillager.CraftType::minecraftHolderToBukkit, CraftVillager.CraftType::bukkitToMinecraftHolder); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java index 8e658a12032c..8f064dabafe4 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java @@ -37,9 +37,14 @@ static final class BuilderImpl implements ItemAdventurePredicate.Builder { @Override public ItemAdventurePredicate.Builder addPredicate(final BlockPredicate predicate) { - this.predicates.add(new net.minecraft.advancements.predicates.BlockPredicate(Optional.ofNullable(predicate.blocks()).map( - blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), blocks) - ), Optional.empty(), Optional.empty(), DataComponentMatchers.ANY)); // TODO DataComponentMatchers + this.predicates.add(new net.minecraft.advancements.predicates.BlockPredicate( + Optional.ofNullable(predicate instanceof final BlockPredicate.MatchesBlocks matchesBlocks ? matchesBlocks.blocks() : null).map( + blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, Conversions.global().lookup(), blocks) + ), + Optional.empty(), + Optional.empty(), + DataComponentMatchers.ANY + )); // TODO DataComponentMatchers return this; } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridgeImpl.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridgeImpl.java new file mode 100644 index 000000000000..22919fb67d18 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/BlockTransformTypesBridgeImpl.java @@ -0,0 +1,17 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.block.stateprovider.BlockStateProvider; + +public final class BlockTransformTypesBridgeImpl implements BlockTransformTypesBridge { + + @Override + public BlockTransformData.Builder blockTransformData(final BlockStateProvider blockStateProvider) { + return new PaperBlockTransformData.BuilderImpl(blockStateProvider); + } + + @Override + public BlockTransformData.Builder blockTransformData(final BlockPredicate predicate, final BlockStateProvider blockStateProvider) { + return new PaperBlockTransformData.BuilderImpl(predicate, blockStateProvider); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformData.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformData.java new file mode 100644 index 000000000000..053f96b5d8df --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformData.java @@ -0,0 +1,232 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.block.BlockPredicate; +import io.papermc.paper.block.stateprovider.BlockStateProvider; +import io.papermc.paper.block.stateprovider.PaperBlockStateProvider; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import net.kyori.adventure.key.Key; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.component.BlockTransformer; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftLootTable; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.util.Handleable; +import org.bukkit.loot.LootTable; +import org.checkerframework.checker.index.qual.NonNegative; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.util.BoundChecker.requireNonNegative; + +public record PaperBlockTransformData( + net.minecraft.core.component.BlockTransformer.BlockTransformData impl +) implements BlockTransformData, Handleable { + + @Override + public net.minecraft.core.component.BlockTransformer.BlockTransformData getHandle() { + return this.impl; + } + + @Override + public BlockStateProvider blockStateProvider() { + return PaperBlockStateProvider.toApi(this.impl.blockStateProvider().value()); + } + + @Override + public Key sound() { + return PaperAdventure.asAdventure(this.impl.sound().value().location()); + } + + @Override + public TransformParticle particle() { + return TransformParticle.valueOf(this.impl.particle().name()); + } + + @Override + public @Unmodifiable List disallowedFaces() { + return this.impl.disallowedFaces().stream().map(CraftBlock::notchToBlockFace).toList(); + } + + @Override + public @Nullable LootTable lootTable() { + return this.impl.loot().map(CraftLootTable::minecraftToBukkit).orElse(null); + } + + @Override + public DropStrategy dropStrategy() { + return DropStrategy.valueOf(this.impl.dropStrategy().name()); + } + + @Override + public boolean updateFromNeighbors() { + return this.impl.updateFromNeighbors(); + } + + @Override + public TransformType transformType() { + return TransformType.valueOf(this.impl.transformType().name()); + } + + @Override + public boolean consumeOnUse() { + return this.impl.consumeOnUse(); + } + + @Override + public @NonNegative int itemDamagePerUse() { + return this.impl.itemDamagePerUse(); + } + + @Override + public Builder toBuilder() { + return new BuilderImpl(this.blockStateProvider()) + .sound(this.sound()) + .particle(this.particle()) + .disallowedFaces(this.disallowedFaces()) + .dropStrategy(this.dropStrategy()) + .updateFromNeighbors(this.updateFromNeighbors()) + .transformType(this.transformType()) + .consumeOnUse(this.consumeOnUse()) + .itemDamagePerUse(this.itemDamagePerUse()); + } + + public static net.minecraft.core.component.BlockTransformer.BlockTransformData toVanilla(final BlockTransformData data) { + if (data instanceof final PaperBlockTransformData paperBlockTransformData) { + return paperBlockTransformData.getHandle(); + } + + return new net.minecraft.core.component.BlockTransformer.BlockTransformData( + Holder.direct(PaperBlockStateProvider.toVanilla(data.blockStateProvider())), + PaperAdventure.resolveSound(data.sound()), + net.minecraft.core.component.BlockTransformer.TransformParticle.valueOf(data.particle().name()), + data.disallowedFaces().stream().map(CraftBlock::blockFaceToNotch).filter(Objects::nonNull).toList(), + java.util.Optional.empty(), + net.minecraft.core.component.BlockTransformer.DropStrategy.valueOf(data.dropStrategy().name()), + data.updateFromNeighbors(), + net.minecraft.core.component.BlockTransformer.TransformType.valueOf(data.transformType().name()), + data.consumeOnUse(), + data.itemDamagePerUse() + ); + } + + static final class BuilderImpl implements Builder { + + private static final Holder DEFAULT_SOUND = BuiltInRegistries.SOUND_EVENT.wrapAsHolder(SoundEvents.EMPTY); + + private final Holder blockStateProvider; + private final @Nullable BlockPredicate predicate; + private Holder sound = DEFAULT_SOUND; + private BlockTransformer.TransformParticle particle = BlockTransformer.TransformParticle.NONE; + private final List disallowedFaces = new ArrayList<>(); + private Optional> lootTable = Optional.empty(); + private BlockTransformer.DropStrategy dropStrategy = BlockTransformer.DropStrategy.FROM_MIDDLE; + private boolean updateFromNeighbors = true; + private BlockTransformer.TransformType transformType = BlockTransformer.TransformType.SINGLE_BLOCK; + private boolean consumeOnUse = true; + private int itemDamagePerUse = 1; + + BuilderImpl(final BlockStateProvider blockStateProvider) { + this(null, blockStateProvider); + } + + BuilderImpl(final @Nullable BlockPredicate predicate, final BlockStateProvider blockStateProvider) { + this.predicate = predicate; + this.blockStateProvider = Holder.direct(PaperBlockStateProvider.toVanilla(blockStateProvider)); + } + + @Override + public Builder sound(final @Nullable Key sound) { + this.sound = (sound == null) ? DEFAULT_SOUND : PaperAdventure.resolveSound(sound); + return this; + } + + @Override + public Builder particle(final TransformParticle particle) { + this.particle = BlockTransformer.TransformParticle.valueOf(asArgument(particle, "particle").name()); + return this; + } + + @Override + public Builder disallowedFaces(final List disallowedFaces) { + this.disallowedFaces.clear(); + disallowedFaces.forEach(this::addDisallowedFace); + return this; + } + + @Override + public Builder addDisallowedFace(final BlockFace disallowedFace) { + this.disallowedFaces.add(asArgument(CraftBlock.blockFaceToNotch(disallowedFace), "disallowedFace")); + return this; + } + + @Override + public Builder lootTable(@Nullable final LootTable lootTable) { + this.lootTable = Optional.ofNullable(lootTable).map(CraftLootTable::bukkitToMinecraft); + return this; + } + + @Override + public Builder dropStrategy(final DropStrategy dropStrategy) { + this.dropStrategy = BlockTransformer.DropStrategy.valueOf(asArgument(dropStrategy, "dropStrategy").name()); + return this; + } + + @Override + public Builder updateFromNeighbors(final boolean updateFromNeighbors) { + this.updateFromNeighbors = updateFromNeighbors; + return this; + } + + @Override + public Builder transformType(final TransformType transformType) { + this.transformType = BlockTransformer.TransformType.valueOf(asArgument(transformType, "transformType").name()); + return this; + } + + @Override + public Builder consumeOnUse(final boolean consumeOnUse) { + this.consumeOnUse = consumeOnUse; + return this; + } + + @Override + public Builder itemDamagePerUse(final @NonNegative int itemDamagePerUse) { + this.itemDamagePerUse = requireNonNegative(itemDamagePerUse, "itemDamagePerUse"); + return this; + } + + @Override + public BlockTransformData build() { + return new PaperBlockTransformData(new net.minecraft.core.component.BlockTransformer.BlockTransformData( + Holder.direct(this.predicate == null + ? this.blockStateProvider.value() + : new net.minecraft.world.level.levelgen.feature.stateproviders.RuleBasedStateProvider( + null, + List.of(new net.minecraft.world.level.levelgen.feature.stateproviders.RuleBasedStateProvider.Rule( + io.papermc.paper.block.PaperBlockPredicate.toVanilla(this.predicate), + this.blockStateProvider + )) + )), + this.sound, + this.particle, + this.disallowedFaces, + this.lootTable, + this.dropStrategy, + this.updateFromNeighbors, + this.transformType, + this.consumeOnUse, + this.itemDamagePerUse + )); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformer.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformer.java new file mode 100644 index 000000000000..473fd520e552 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/PaperBlockTransformer.java @@ -0,0 +1,38 @@ +package io.papermc.paper.datacomponent.item.blocktransformer; + +import java.util.List; +import io.papermc.paper.registry.HolderableBase; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jetbrains.annotations.Unmodifiable; + +public final class PaperBlockTransformer extends HolderableBase implements BlockTransformer { + + public static BlockTransformer minecraftHolderToBukkit(final Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.BLOCK_TRANSFORMER); + } + + public static Holder bukkitToMinecraftHolder(final BlockTransformer bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + public PaperBlockTransformer(final Holder holder) { + super(holder); + } + + @Override + public @Unmodifiable List transforms() { + return this.getHandle().transforms().stream().map(PaperBlockTransformData::new).map(BlockTransformData.class::cast).toList(); + } + + public static Holder abukkitToMinecraftHolder(final BlockTransformer blockTransformer) { + if (!(blockTransformer instanceof final PaperBlockTransformer paper)) { + throw new IllegalArgumentException("Custom block_transformer values must be registered in minecraft:block_transformer before syncing to clients"); + } + if (paper.getHolder().kind() != Holder.Kind.REFERENCE) { + throw new IllegalArgumentException("Custom block_transformer values must be registered in minecraft:block_transformer before syncing to clients"); + } + return paper.getHolder(); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java new file mode 100644 index 000000000000..08508546229c --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocktransformer/package-info.java @@ -0,0 +1,7 @@ +/** + * Relating to block attacks for components. + */ +@NullMarked +package io.papermc.paper.datacomponent.item.blocktransformer; + +import org.jspecify.annotations.NullMarked; 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 cb0c47449474..6a6115aa2960 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 @@ -6,11 +6,14 @@ import io.papermc.paper.block.pot.PotPatternType; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.PaperDataComponentType; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; +import io.papermc.paper.datacomponent.item.blocktransformer.PaperBlockTransformer; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.dialog.PaperDialog; import io.papermc.paper.entity.poi.PaperPoiType; import io.papermc.paper.entity.poi.PoiTypes; import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry; +import io.papermc.paper.registry.data.PaperBlockTransformerRegistryEntry; import io.papermc.paper.registry.data.PaperCatTypeRegistryEntry; import io.papermc.paper.registry.data.PaperChickenVariantRegistryEntry; import io.papermc.paper.registry.data.PaperCowVariantRegistryEntry; @@ -159,6 +162,7 @@ public final class PaperRegistries { start(Registries.ZOMBIE_NAUTILUS_VARIANT, RegistryKey.ZOMBIE_NAUTILUS_VARIANT).craft(ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant::new).writable(PaperZombieNautilusVariantRegistryEntry.PaperBuilder::new), start(Registries.SULFUR_CUBE_ARCHETYPE, RegistryKey.SULFUR_CUBE_ARCHETYPE).craft(SulfurCube.Archetype.class, CraftSulfurCube.CraftArchetype::new).writable(PaperSulfurCubeArchetypeRegistryEntry.PaperBuilder::new), start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new), + start(Registries.BLOCK_TRANSFORMER, RegistryKey.BLOCK_TRANSFORMER).craft(BlockTransformer.class, PaperBlockTransformer::new, true).writable(PaperBlockTransformerRegistryEntry.PaperBuilder::new), // api-only start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType), diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBlockTransformerRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBlockTransformerRegistryEntry.java new file mode 100644 index 000000000000..6009a2f85890 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBlockTransformerRegistryEntry.java @@ -0,0 +1,59 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformData; +import io.papermc.paper.datacomponent.item.blocktransformer.PaperBlockTransformData; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.data.util.Conversions; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.core.component.BlockTransformer; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.registry.data.util.Checks.asConfigured; + +public class PaperBlockTransformerRegistryEntry implements BlockTransformerRegistryEntry { + + protected @Nullable List transforms; + + public PaperBlockTransformerRegistryEntry(final Conversions conversions, final @Nullable BlockTransformer internal) { + if (internal == null) { + return; + } + this.transforms = new ArrayList<>(internal.transforms().stream().map(PaperBlockTransformData::new).map(BlockTransformData.class::cast).toList()); + } + + @Override + public List transforms() { + return asConfigured(this.transforms, "transforms"); + } + + public static final class PaperBuilder extends PaperBlockTransformerRegistryEntry implements Builder, PaperRegistryBuilder { + + public PaperBuilder(final Conversions conversions, final @Nullable BlockTransformer internal) { + super(conversions, internal); + } + + @Override + public Builder addTransform(final BlockTransformData transform) { + if (this.transforms == null) { + this.transforms = new ArrayList<>(); + } + this.transforms.add(asArgument(transform, "transform")); + return this; + } + + @Override + public Builder addTransforms(final List transforms) { + for (final BlockTransformData transform : asArgument(transforms, "transforms")) { + this.addTransform(transform); + } + return this; + } + + @Override + public BlockTransformer build() { + return new BlockTransformer(asConfigured(this.transforms, "transforms").stream().map(PaperBlockTransformData::toVanilla).toList()); + } + } +} diff --git a/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformTypesBridge b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformTypesBridge new file mode 100644 index 000000000000..1e530aad5348 --- /dev/null +++ b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformTypesBridge @@ -0,0 +1 @@ +io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformTypesBridgeImpl 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 2ebcb1f1d66e..028909c01d99 100644 --- a/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java +++ b/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java @@ -58,7 +58,6 @@ public static void populateIgnored() { DataComponents.LOCK, DataComponents.CREATIVE_SLOT_LOCK, DataComponents.ADDITIONAL_TRADE_COST, - DataComponents.BLOCK_TRANSFORMER, DataComponents.COMPOSTABLE, DataComponents.COOKING_FUEL, DataComponents.BREWING_FUEL 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 490d53d9e8f7..6780e1998881 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 @@ -5,6 +5,9 @@ import io.papermc.paper.block.pot.PotPatternType; import io.papermc.paper.block.pot.PotPatternTypes; import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformer; +import io.papermc.paper.datacomponent.item.blocktransformer.BlockTransformers; +import io.papermc.paper.datacomponent.item.blocktransformer.PaperBlockTransformer; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.dialog.PaperDialog; import io.papermc.paper.entity.poi.PaperPoiType; @@ -170,6 +173,7 @@ public Object[] get() { register(Registries.GAME_RULE, GameRule.class, GameRules.class, CraftGameRule.class, net.minecraft.world.level.gamerules.GameRule.class); register(Registries.POINT_OF_INTEREST_TYPE, PoiType.class, PoiTypes.class, PaperPoiType.class, net.minecraft.world.entity.ai.village.poi.PoiType.class); register(Registries.SULFUR_CUBE_ARCHETYPE, SulfurCube.Archetype.class, CraftSulfurCube.CraftArchetype.class, SulfurCubeArchetype.class); + register(Registries.BLOCK_TRANSFORMER, BlockTransformer.class, BlockTransformers.class, PaperBlockTransformer.class, net.minecraft.core.component.BlockTransformer.class); } private static void register(ResourceKey> registryKey, Class api, Class impl, Class internal) {