From f1dbed072cc5bf0f3b4f37980c1bd4f6599bdacb Mon Sep 17 00:00:00 2001
From: Pedro <3602279+Doc94@users.noreply.github.com>
Date: Sun, 18 May 2025 10:31:34 -0400
Subject: [PATCH] Implement BlocksAttack DamageReduction and ItemDamage
(#12538)
---
.../datacomponent/item/BlocksAttacks.java | 71 ++++++++++++---
.../blocksattacks/BlocksAttacksBridge.java | 21 +++++
.../item/blocksattacks/DamageReduction.java | 78 +++++++++++++++++
.../blocksattacks/ItemDamageFunction.java | 71 +++++++++++++++
.../item/PaperBlocksAttacks.java | 51 +++++++----
.../BlocksAttacksBridgeImpl.java | 19 ++++
.../blocksattacks/PaperDamageReduction.java | 87 +++++++++++++++++++
.../PaperItemDamageFunction.java | 70 +++++++++++++++
.../item/blocksattacks/package-info.java | 7 ++
...ent.item.blocksattacks.BlocksAttacksBridge | 1 +
10 files changed, 448 insertions(+), 28 deletions(-)
create mode 100644 paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridge.java
create mode 100644 paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/DamageReduction.java
create mode 100644 paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/ItemDamageFunction.java
create mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridgeImpl.java
create mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperDamageReduction.java
create mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperItemDamageFunction.java
create mode 100644 paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/package-info.java
create mode 100644 paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridge
diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java
index bf53d0e0b0..d7372673ac 100644
--- a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java
+++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java
@@ -1,6 +1,8 @@
package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
+import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction;
+import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction;
import io.papermc.paper.registry.tag.TagKey;
import net.kyori.adventure.key.Key;
import org.bukkit.damage.DamageType;
@@ -8,8 +10,13 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
+import java.util.List;
-// TODO
+/**
+ * Holds block attacks to the holding player like Shield.
+ *
+ * @see io.papermc.paper.datacomponent.DataComponentTypes#BLOCKS_ATTACKS
+ */
@NullMarked
@ApiStatus.Experimental
@ApiStatus.NonExtendable
@@ -20,19 +27,59 @@ public interface BlocksAttacks {
return ItemComponentTypesBridge.bridge().blocksAttacks();
}
+ /**
+ * Gets the amount of time (in seconds) that use must be held before successfully blocking attacks.
+ *
+ * @return the delay in seconds
+ */
float blockDelaySeconds();
+ /**
+ * Gets the multiplier applied to the cooldown time for the item when attacked by a disabling attack (the multiplier for {@link Weapon#disableBlockingForSeconds()}).
+ *
+ * If set to 0, this item can never be disabled by attacks.
+ *
+ * @return the multiplier for the cooldown time
+ */
float disableCooldownScale();
- //List damageReductions();
+ /**
+ * Gets a list of {@link DamageReduction} of how much damage should be blocked in a given attack.
+ *
+ * @return a list of damage reductions
+ */
+ List damageReductions();
- //ItemDamageFunction itemDamage();
+ /**
+ * Gets how much damage should be applied to the item from a given attack.
+ *
+ * @return the damage function
+ */
+ ItemDamageFunction itemDamage();
- @Nullable TagKey bypassedBy();
+ /**
+ * Gets the DamageType that can bypass the blocking.
+ *
+ * @return a damage type tag key, or null if there is no such tag key
+ */
+ @Nullable
+ TagKey bypassedBy();
- @Nullable Key blockSound();
+ /**
+ * Gets the key sound to play when an attack is successfully blocked.
+ *
+ * @return a key of the sound
+ */
+ @Nullable
+ Key blockSound();
- @Nullable Key disableSound();
+ /**
+ * Gets the key sound to play when the item goes on its disabled cooldown due to an attack.
+ *
+ * @return a key of the sound
+ */
+ @Nullable
+ Key disableSound();
/**
* Builder for {@link BlocksAttacks}.
@@ -47,14 +94,14 @@ public interface BlocksAttacks {
@Contract(value = "_ -> this", mutates = "this")
Builder disableCooldownScale(float scale);
- //@Contract(value = "_ -> this", mutates = "this")
- //Builder addDamageReduction(DamageReduction reduction);
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder addDamageReduction(DamageReduction reduction);
- //@Contract(value = "_ -> this", mutates = "this")
- //Builder damageReductions(List reductions);
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder damageReductions(List reductions);
- //@Contract(value = "_ -> this", mutates = "this")
- //Builder itemDamage(ItemDamageFunction function);
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder itemDamage(ItemDamageFunction function);
@Contract(value = "_ -> this", mutates = "this")
Builder bypassedBy(@Nullable TagKey bypassedBy);
diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridge.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridge.java
new file mode 100644
index 0000000000..aafc085c91
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridge.java
@@ -0,0 +1,21 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import java.util.Optional;
+import java.util.ServiceLoader;
+
+@NullMarked
+@ApiStatus.Internal
+interface BlocksAttacksBridge {
+
+ Optional BRIDGE = ServiceLoader.load(BlocksAttacksBridge.class).findFirst();
+
+ static BlocksAttacksBridge bridge() {
+ return BRIDGE.orElseThrow();
+ }
+
+ DamageReduction.Builder blocksAttacksDamageReduction();
+
+ ItemDamageFunction.Builder blocksAttacksItemDamageFunction();
+}
diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/DamageReduction.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/DamageReduction.java
new file mode 100644
index 0000000000..fbf4a03782
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/DamageReduction.java
@@ -0,0 +1,78 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+import io.papermc.paper.registry.set.RegistryKeySet;
+import org.bukkit.damage.DamageType;
+import org.checkerframework.checker.index.qual.Positive;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Hold how much damage should be blocked in a given attack.
+ *
+ * @see io.papermc.paper.datacomponent.DataComponentTypes#BLOCKS_ATTACKS
+ * @see io.papermc.paper.datacomponent.item.BlocksAttacks#damageReductions()
+ */
+@NullMarked
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface DamageReduction {
+
+ @Contract(value = "-> new", pure = true)
+ static DamageReduction.Builder damageReduction() {
+ return BlocksAttacksBridge.bridge().blocksAttacksDamageReduction();
+ }
+
+ /**
+ * The damage types to block.
+ *
+ * @return the set of damage type
+ */
+ @Nullable
+ RegistryKeySet type();
+
+ /**
+ * Get the maximum angle between the users facing direction and the direction of the incoming attack to be blocked.
+ *
+ * @return the angle
+ */
+ @Positive
+ float horizontalBlockingAngle();
+
+ /**
+ * Get the constant amount of damage to be blocked.
+ *
+ * @return the base
+ */
+ float base();
+
+ /**
+ * Get the fraction of the dealt damage to be blocked.
+ *
+ * @return the factor
+ */
+ float factor();
+
+ /**
+ * Builder for {@link DamageReduction}.
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends DataComponentBuilder {
+
+ @Contract(value = "_ -> this", mutates = "this")
+ DamageReduction.Builder type(RegistryKeySet type);
+
+ @Contract(value = "_ -> this", mutates = "this")
+ DamageReduction.Builder horizontalBlockingAngle(@Positive float horizontalBlockingAngle);
+
+ @Contract(value = "_ -> this", mutates = "this")
+ DamageReduction.Builder base(float base);
+
+ @Contract(value = "_ -> this", mutates = "this")
+ DamageReduction.Builder factor(float factor);
+ }
+
+}
diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/ItemDamageFunction.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/ItemDamageFunction.java
new file mode 100644
index 0000000000..965b12fc66
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/ItemDamageFunction.java
@@ -0,0 +1,71 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import io.papermc.paper.datacomponent.DataComponentBuilder;
+import org.checkerframework.checker.index.qual.NonNegative;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Hold how much damage should be applied to the item from a given attack.
+ *
+ * @see io.papermc.paper.datacomponent.DataComponentTypes#BLOCKS_ATTACKS
+ * @see io.papermc.paper.datacomponent.item.BlocksAttacks#itemDamage()
+ */
+@NullMarked
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface ItemDamageFunction {
+
+ @Contract(value = "-> new", pure = true)
+ static ItemDamageFunction.Builder itemDamageFunction() {
+ return BlocksAttacksBridge.bridge().blocksAttacksItemDamageFunction();
+ }
+
+ /**
+ * Get the minimum amount of damage dealt by the attack before item damage is applied to the item.
+ *
+ * @return the threshold
+ */
+ @NonNegative
+ float threshold();
+
+ /**
+ * Get the constant amount of damage applied to the item, if threshold is passed.
+ *
+ * @return the base
+ */
+ float base();
+
+ /**
+ * Get the fraction of the dealt damage that should be applied to the item, if threshold is passed.
+ *
+ * @return the base
+ */
+ float factor();
+
+ /**
+ * Get the damage to apply for the item.
+ *
+ * @apiNote this doesn't apply enchantments like {@link org.bukkit.enchantments.Enchantment#UNBREAKING}}
+ * @return the damage to apply
+ */
+ int damageToApply(float damage);
+
+ /**
+ * Builder for {@link ItemDamageFunction}.
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends DataComponentBuilder {
+
+ @Contract(value = "_ -> this", mutates = "this")
+ ItemDamageFunction.Builder threshold(@NonNegative final float threshold);
+
+ @Contract(value = "_ -> this", mutates = "this")
+ ItemDamageFunction.Builder base(final float base);
+
+ @Contract(value = "_ -> this", mutates = "this")
+ ItemDamageFunction.Builder factor(final float factor);
+ }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java
index 1748518bbc..b4b9df9d85 100644
--- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java
+++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java
@@ -2,8 +2,13 @@ package io.papermc.paper.datacomponent.item;
import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction;
+import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction;
+import io.papermc.paper.datacomponent.item.blocksattacks.PaperDamageReduction;
+import io.papermc.paper.datacomponent.item.blocksattacks.PaperItemDamageFunction;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.tag.TagKey;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import net.kyori.adventure.key.Key;
@@ -30,6 +35,16 @@ public record PaperBlocksAttacks(
return this.impl.disableCooldownScale();
}
+ @Override
+ public List damageReductions() {
+ return this.impl.damageReductions().stream().map(PaperDamageReduction::new).map(paperDamageReduction -> ((DamageReduction) paperDamageReduction)).toList();
+ }
+
+ @Override
+ public ItemDamageFunction itemDamage() {
+ return new PaperItemDamageFunction(this.impl.itemDamage());
+ }
+
@Override
public @Nullable TagKey bypassedBy() {
final Optional> tagKey = this.impl.bypassedBy().map(PaperRegistries::fromNms);
@@ -50,8 +65,8 @@ public record PaperBlocksAttacks(
private float blockDelaySeconds;
private float disableCooldownScale = 1.0F;
- //private List damageReductions = List.of();
- //private ItemDamageFunction itemDamage = ItemDamageFunction.DEFAULT;
+ private List damageReductions = new ArrayList<>();
+ private ItemDamageFunction itemDamage = new PaperItemDamageFunction(net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction.DEFAULT);
private @Nullable TagKey bypassedBy;
private @Nullable Key blockSound;
private @Nullable Key disableSound;
@@ -70,15 +85,18 @@ public record PaperBlocksAttacks(
return this;
}
- //@Override
- //public Builder addDamageReduction(final DamageReduction reduction) {
- // return null;
- //}
+ @Override
+ public Builder addDamageReduction(final DamageReduction reduction) {
+ Preconditions.checkArgument(reduction.horizontalBlockingAngle() >= 0, "horizontalBlockingAngle must be non-negative, was %s", reduction.horizontalBlockingAngle());
+ this.damageReductions.add(reduction);
+ return this;
+ }
- //@Override
- //public Builder itemDamage(final ItemDamageFunction function) {
- // return null;
- //}
+ @Override
+ public Builder itemDamage(final ItemDamageFunction function) {
+ this.itemDamage = function;
+ return this;
+ }
@Override
public Builder bypassedBy(@Nullable final TagKey bypassedBy) {
@@ -98,18 +116,19 @@ public record PaperBlocksAttacks(
return this;
}
- //@Override
- //public Builder damageReductions(final List reductions) {
- // return null;
- //}
+ @Override
+ public Builder damageReductions(final List reductions) {
+ this.damageReductions = new ArrayList<>(reductions);
+ return this;
+ }
@Override
public BlocksAttacks build() {
return new PaperBlocksAttacks(new net.minecraft.world.item.component.BlocksAttacks(
this.blockDelaySeconds,
this.disableCooldownScale,
- List.of(), // TODO
- net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction.DEFAULT, // TODO
+ this.damageReductions.stream().map(damageReduction -> ((PaperDamageReduction) damageReduction).getHandle()).toList(),
+ ((PaperItemDamageFunction) itemDamage).getHandle(),
Optional.ofNullable(this.bypassedBy).map(PaperRegistries::toNms),
Optional.ofNullable(this.blockSound).map(PaperAdventure::resolveSound),
Optional.ofNullable(this.disableSound).map(PaperAdventure::resolveSound)
diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridgeImpl.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridgeImpl.java
new file mode 100644
index 0000000000..865563be36
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/BlocksAttacksBridgeImpl.java
@@ -0,0 +1,19 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+public class BlocksAttacksBridgeImpl implements BlocksAttacksBridge {
+
+ @Override
+ public DamageReduction.Builder blocksAttacksDamageReduction() {
+ return new PaperDamageReduction.BuilderImpl();
+ }
+
+ @Override
+ public ItemDamageFunction.Builder blocksAttacksItemDamageFunction() {
+ return new PaperItemDamageFunction.BuilderImpl();
+ }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperDamageReduction.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperDamageReduction.java
new file mode 100644
index 0000000000..7da287938e
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperDamageReduction.java
@@ -0,0 +1,87 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.set.PaperRegistrySets;
+import io.papermc.paper.registry.set.RegistryKeySet;
+import net.minecraft.core.HolderSet;
+import net.minecraft.core.registries.Registries;
+import org.bukkit.craftbukkit.util.Handleable;
+import org.bukkit.damage.DamageType;
+import org.checkerframework.checker.index.qual.Positive;
+import org.jetbrains.annotations.Nullable;
+import java.util.Optional;
+
+public record PaperDamageReduction(
+ net.minecraft.world.item.component.BlocksAttacks.DamageReduction impl
+) implements DamageReduction, Handleable {
+
+ @Override
+ public net.minecraft.world.item.component.BlocksAttacks.DamageReduction getHandle() {
+ return this.impl;
+ }
+
+ @Override
+ public @Nullable RegistryKeySet type() {
+ return this.impl.type().map((set) -> PaperRegistrySets.convertToApi(RegistryKey.DAMAGE_TYPE, set)).orElse(null);
+ }
+
+ @Override
+ public @Positive float horizontalBlockingAngle() {
+ return this.impl.horizontalBlockingAngle();
+ }
+
+ @Override
+ public float base() {
+ return this.impl.base();
+ }
+
+ @Override
+ public float factor() {
+ return this.impl.factor();
+ }
+
+ static final class BuilderImpl implements Builder {
+
+ private Optional> type = Optional.empty();
+ private float horizontalBlockingAngle = 90f;
+ private float base = 0;
+ private float factor = 0;
+
+ @Override
+ public Builder type(final @Nullable RegistryKeySet type) {
+ this.type = Optional.ofNullable(type)
+ .map((set) -> PaperRegistrySets.convertToNms(Registries.DAMAGE_TYPE, net.minecraft.server.MinecraftServer.getServer().registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE).lookupProvider, set));
+ return this;
+ }
+
+ @Override
+ public Builder horizontalBlockingAngle(@Positive final float horizontalBlockingAngle) {
+ Preconditions.checkArgument(horizontalBlockingAngle > 0, "horizontalBlockingAngle must be positive and not zero, was %s", horizontalBlockingAngle);
+ this.horizontalBlockingAngle = horizontalBlockingAngle;
+ return this;
+ }
+
+ @Override
+ public Builder base(final float base) {
+ this.base = base;
+ return this;
+ }
+
+ @Override
+ public Builder factor(final float factor) {
+ this.factor = factor;
+ return this;
+ }
+
+ @Override
+ public DamageReduction build() {
+ return new PaperDamageReduction(new net.minecraft.world.item.component.BlocksAttacks.DamageReduction(
+ this.horizontalBlockingAngle,
+ this.type,
+ this.base,
+ this.factor
+ ));
+ }
+ }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperItemDamageFunction.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperItemDamageFunction.java
new file mode 100644
index 0000000000..515eec1599
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/PaperItemDamageFunction.java
@@ -0,0 +1,70 @@
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import com.google.common.base.Preconditions;
+import org.bukkit.craftbukkit.util.Handleable;
+import org.checkerframework.checker.index.qual.NonNegative;
+
+public record PaperItemDamageFunction(
+ net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction impl
+) implements ItemDamageFunction, Handleable {
+
+ @Override
+ public net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction getHandle() {
+ return this.impl;
+ }
+
+ @Override
+ public @NonNegative float threshold() {
+ return this.impl.threshold();
+ }
+
+ @Override
+ public float base() {
+ return this.impl.base();
+ }
+
+ @Override
+ public float factor() {
+ return this.impl.factor();
+ }
+
+ @Override
+ public int damageToApply(final float damage) {
+ return this.impl.apply(damage);
+ }
+
+ static final class BuilderImpl implements Builder {
+
+ private float threshold;
+ private float base;
+ private float factor;
+
+ @Override
+ public Builder threshold(@NonNegative final float threshold) {
+ Preconditions.checkArgument(threshold >= 0, "threshold must be non-negative, was %s", threshold);
+ this.threshold = threshold;
+ return this;
+ }
+
+ @Override
+ public Builder base(final float base) {
+ this.base = base;
+ return this;
+ }
+
+ @Override
+ public Builder factor(final float factor) {
+ this.factor = factor;
+ return this;
+ }
+
+ @Override
+ public ItemDamageFunction build() {
+ return new PaperItemDamageFunction(new net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction(
+ this.threshold,
+ this.base,
+ this.factor
+ ));
+ }
+ }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/package-info.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/package-info.java
new file mode 100644
index 0000000000..d0fca5341f
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/blocksattacks/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Relating to block attacks for components.
+ */
+@NullMarked
+package io.papermc.paper.datacomponent.item.blocksattacks;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridge b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridge
new file mode 100644
index 0000000000..02e0ef0649
--- /dev/null
+++ b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridge
@@ -0,0 +1 @@
+io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridgeImpl