Implement BlocksAttack DamageReduction and ItemDamage (#12538)

This commit is contained in:
Pedro 2025-05-18 10:31:34 -04:00 committed by GitHub
parent 28d7df75ac
commit f1dbed072c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 448 additions and 28 deletions

View File

@ -1,6 +1,8 @@
package io.papermc.paper.datacomponent.item; package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder; 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 io.papermc.paper.registry.tag.TagKey;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import org.bukkit.damage.DamageType; import org.bukkit.damage.DamageType;
@ -8,8 +10,13 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable; 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 @NullMarked
@ApiStatus.Experimental @ApiStatus.Experimental
@ApiStatus.NonExtendable @ApiStatus.NonExtendable
@ -20,19 +27,59 @@ public interface BlocksAttacks {
return ItemComponentTypesBridge.bridge().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(); 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()}).
* <br>
* If set to 0, this item can never be disabled by attacks.
*
* @return the multiplier for the cooldown time
*/
float disableCooldownScale(); float disableCooldownScale();
//List<DamageReduction> 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<DamageReduction> 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<DamageType> 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<DamageType> 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}. * Builder for {@link BlocksAttacks}.
@ -47,14 +94,14 @@ public interface BlocksAttacks {
@Contract(value = "_ -> this", mutates = "this") @Contract(value = "_ -> this", mutates = "this")
Builder disableCooldownScale(float scale); Builder disableCooldownScale(float scale);
//@Contract(value = "_ -> this", mutates = "this") @Contract(value = "_ -> this", mutates = "this")
//Builder addDamageReduction(DamageReduction reduction); Builder addDamageReduction(DamageReduction reduction);
//@Contract(value = "_ -> this", mutates = "this") @Contract(value = "_ -> this", mutates = "this")
//Builder damageReductions(List<DamageReduction> reductions); Builder damageReductions(List<DamageReduction> reductions);
//@Contract(value = "_ -> this", mutates = "this") @Contract(value = "_ -> this", mutates = "this")
//Builder itemDamage(ItemDamageFunction function); Builder itemDamage(ItemDamageFunction function);
@Contract(value = "_ -> this", mutates = "this") @Contract(value = "_ -> this", mutates = "this")
Builder bypassedBy(@Nullable TagKey<DamageType> bypassedBy); Builder bypassedBy(@Nullable TagKey<DamageType> bypassedBy);

View File

@ -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<BlocksAttacksBridge> BRIDGE = ServiceLoader.load(BlocksAttacksBridge.class).findFirst();
static BlocksAttacksBridge bridge() {
return BRIDGE.orElseThrow();
}
DamageReduction.Builder blocksAttacksDamageReduction();
ItemDamageFunction.Builder blocksAttacksItemDamageFunction();
}

View File

@ -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<DamageType> 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<DamageReduction> {
@Contract(value = "_ -> this", mutates = "this")
DamageReduction.Builder type(RegistryKeySet<DamageType> 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);
}
}

View File

@ -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<ItemDamageFunction> {
@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);
}
}

View File

@ -2,8 +2,13 @@ package io.papermc.paper.datacomponent.item;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure; 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.PaperRegistries;
import io.papermc.paper.registry.tag.TagKey; import io.papermc.paper.registry.tag.TagKey;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
@ -30,6 +35,16 @@ public record PaperBlocksAttacks(
return this.impl.disableCooldownScale(); return this.impl.disableCooldownScale();
} }
@Override
public List<DamageReduction> 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 @Override
public @Nullable TagKey<DamageType> bypassedBy() { public @Nullable TagKey<DamageType> bypassedBy() {
final Optional<TagKey<DamageType>> tagKey = this.impl.bypassedBy().map(PaperRegistries::fromNms); final Optional<TagKey<DamageType>> tagKey = this.impl.bypassedBy().map(PaperRegistries::fromNms);
@ -50,8 +65,8 @@ public record PaperBlocksAttacks(
private float blockDelaySeconds; private float blockDelaySeconds;
private float disableCooldownScale = 1.0F; private float disableCooldownScale = 1.0F;
//private List<DamageReduction> damageReductions = List.of(); private List<DamageReduction> damageReductions = new ArrayList<>();
//private ItemDamageFunction itemDamage = ItemDamageFunction.DEFAULT; private ItemDamageFunction itemDamage = new PaperItemDamageFunction(net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction.DEFAULT);
private @Nullable TagKey<DamageType> bypassedBy; private @Nullable TagKey<DamageType> bypassedBy;
private @Nullable Key blockSound; private @Nullable Key blockSound;
private @Nullable Key disableSound; private @Nullable Key disableSound;
@ -70,15 +85,18 @@ public record PaperBlocksAttacks(
return this; return this;
} }
//@Override @Override
//public Builder addDamageReduction(final DamageReduction reduction) { public Builder addDamageReduction(final DamageReduction reduction) {
// return null; Preconditions.checkArgument(reduction.horizontalBlockingAngle() >= 0, "horizontalBlockingAngle must be non-negative, was %s", reduction.horizontalBlockingAngle());
//} this.damageReductions.add(reduction);
return this;
}
//@Override @Override
//public Builder itemDamage(final ItemDamageFunction function) { public Builder itemDamage(final ItemDamageFunction function) {
// return null; this.itemDamage = function;
//} return this;
}
@Override @Override
public Builder bypassedBy(@Nullable final TagKey<DamageType> bypassedBy) { public Builder bypassedBy(@Nullable final TagKey<DamageType> bypassedBy) {
@ -98,18 +116,19 @@ public record PaperBlocksAttacks(
return this; return this;
} }
//@Override @Override
//public Builder damageReductions(final List<DamageReduction> reductions) { public Builder damageReductions(final List<DamageReduction> reductions) {
// return null; this.damageReductions = new ArrayList<>(reductions);
//} return this;
}
@Override @Override
public BlocksAttacks build() { public BlocksAttacks build() {
return new PaperBlocksAttacks(new net.minecraft.world.item.component.BlocksAttacks( return new PaperBlocksAttacks(new net.minecraft.world.item.component.BlocksAttacks(
this.blockDelaySeconds, this.blockDelaySeconds,
this.disableCooldownScale, this.disableCooldownScale,
List.of(), // TODO this.damageReductions.stream().map(damageReduction -> ((PaperDamageReduction) damageReduction).getHandle()).toList(),
net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction.DEFAULT, // TODO ((PaperItemDamageFunction) itemDamage).getHandle(),
Optional.ofNullable(this.bypassedBy).map(PaperRegistries::toNms), Optional.ofNullable(this.bypassedBy).map(PaperRegistries::toNms),
Optional.ofNullable(this.blockSound).map(PaperAdventure::resolveSound), Optional.ofNullable(this.blockSound).map(PaperAdventure::resolveSound),
Optional.ofNullable(this.disableSound).map(PaperAdventure::resolveSound) Optional.ofNullable(this.disableSound).map(PaperAdventure::resolveSound)

View File

@ -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();
}
}

View File

@ -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<net.minecraft.world.item.component.BlocksAttacks.DamageReduction> {
@Override
public net.minecraft.world.item.component.BlocksAttacks.DamageReduction getHandle() {
return this.impl;
}
@Override
public @Nullable RegistryKeySet<DamageType> 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<HolderSet<net.minecraft.world.damagesource.DamageType>> type = Optional.empty();
private float horizontalBlockingAngle = 90f;
private float base = 0;
private float factor = 0;
@Override
public Builder type(final @Nullable RegistryKeySet<DamageType> 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
));
}
}
}

View File

@ -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<net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction> {
@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
));
}
}
}

View File

@ -0,0 +1,7 @@
/**
* Relating to block attacks for components.
*/
@NullMarked
package io.papermc.paper.datacomponent.item.blocksattacks;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1 @@
io.papermc.paper.datacomponent.item.blocksattacks.BlocksAttacksBridgeImpl