diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java
new file mode 100644
index 000000000..511da534a
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java
@@ -0,0 +1,39 @@
+package io.papermc.paper;
+
+import net.kyori.adventure.util.Services;
+import org.bukkit.damage.DamageEffect;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Static bridge to the server internals.
+ *
+ * Any and all methods in here are *not* to be called by plugin developers, may change at any time and may generally
+ * cause issues when called under unexpected circumstances.
+ */
+@ApiStatus.Internal
+@NullMarked
+public interface InternalAPIBridge {
+
+ /**
+ * Yields the instance of this API bridge by lazily requesting it from the java service loader API.
+ *
+ * @return the instance.
+ */
+ static InternalAPIBridge get() {
+ class Holder {
+ public static final InternalAPIBridge INSTANCE = Services.service(InternalAPIBridge.class).orElseThrow();
+ }
+
+ return Holder.INSTANCE;
+ }
+
+ /**
+ * Creates a damage effect instance for the passed key.
+ *
+ * @param key the string key.
+ * @return the damage effect.
+ */
+ DamageEffect getDamageEffect(String key);
+}
+
diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java
new file mode 100644
index 000000000..fd2d7fffa
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java
@@ -0,0 +1,120 @@
+package io.papermc.paper.registry.data;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import org.bukkit.damage.DamageEffect;
+import org.bukkit.damage.DamageScaling;
+import org.bukkit.damage.DamageType;
+import org.bukkit.damage.DeathMessageType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+
+/**
+ * A data-centric version-specific registry entry for the {@link DamageType} type.
+ */
+@ApiStatus.Experimental
+@ApiStatus.NonExtendable
+public interface DamageTypeRegistryEntry {
+
+ /**
+ * Provides part of the death message translation key. (death.attack.<message_id>)
+ *
+ * Note The translation key is only used if
+ * {@link #deathMessageType()} is {@link DeathMessageType#DEFAULT}
+ *
+ * @return part of the translation key
+ */
+ String messageId();
+
+ /**
+ * Provides the amount of hunger exhaustion caused by this damage type.
+ *
+ * @return the exhaustion
+ */
+ float exhaustion();
+
+ /**
+ * Provides the {@link DamageScaling} for this damage type.
+ *
+ * @return the damage scaling
+ */
+ DamageScaling damageScaling();
+
+ /**
+ * Provides the {@link DamageEffect} for this damage type.
+ *
+ * @return the damage effect
+ */
+ DamageEffect damageEffect();
+
+ /**
+ * Provides the {@link DeathMessageType} for this damage type.
+ *
+ * @return the death message type
+ */
+ DeathMessageType deathMessageType();
+
+ /**
+ * A mutable builder for the {@link DamageTypeRegistryEntry} plugins may change in applicable registry events.
+ *
+ * The following values are required for each builder:
+ *
+ * - {@link #messageId(String)}
+ * - {@link #exhaustion(float)}
+ * - {@link #damageScaling(DamageScaling)}
+ *
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends DamageTypeRegistryEntry, RegistryBuilder {
+
+ /**
+ * Sets part of the death message translation key.
+ *
+ * @return this builder instance.
+ * @see DamageTypeRegistryEntry#messageId()
+ * @see DamageType#getTranslationKey()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder messageId(String messageId);
+
+ /**
+ * Sets the amount of hunger exhaustion caused by this damage type.
+ *
+ * @return this builder instance.
+ * @see DamageTypeRegistryEntry#exhaustion()
+ * @see DamageType#getExhaustion()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder exhaustion(float exhaustion);
+
+ /**
+ * Sets the {@link DamageScaling} for this damage type.
+ *
+ * @return this builder instance.
+ * @see DamageTypeRegistryEntry#damageScaling()
+ * @see DamageType#getDamageScaling()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder damageScaling(DamageScaling scaling);
+
+ /**
+ * Sets the {@link DamageEffect} for this damage type.
+ *
+ * @return this builder instance.
+ * @see DamageTypeRegistryEntry#damageEffect()
+ * @see DamageType#getDamageEffect()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder damageEffect(DamageEffect effect);
+
+ /**
+ * Sets the {@link DeathMessageType} for this damage type.
+ *
+ * @return this builder instance.
+ * @see DamageTypeRegistryEntry#deathMessageType()
+ * @see DamageType#getDeathMessageType()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder deathMessageType(DeathMessageType deathMessageType);
+ }
+}
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 cfda2a7e2..d15581579 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
@@ -2,12 +2,14 @@ package io.papermc.paper.registry.event;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
+import io.papermc.paper.registry.data.DamageTypeRegistryEntry;
import io.papermc.paper.registry.data.EnchantmentRegistryEntry;
import io.papermc.paper.registry.data.GameEventRegistryEntry;
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
import org.bukkit.Art;
import org.bukkit.GameEvent;
import org.bukkit.block.banner.PatternType;
+import org.bukkit.damage.DamageType;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@@ -26,6 +28,7 @@ public final class RegistryEvents {
public static final RegistryEventProvider ENCHANTMENT = create(RegistryKey.ENCHANTMENT);
public static final RegistryEventProvider PAINTING_VARIANT = create(RegistryKey.PAINTING_VARIANT);
public static final RegistryEventProvider BANNER_PATTERN = create(RegistryKey.BANNER_PATTERN);
+ public static final RegistryEventProvider DAMAGE_TYPE = create(RegistryKey.DAMAGE_TYPE);
private RegistryEvents() {
}
diff --git a/paper-api/src/main/java/org/bukkit/UnsafeValues.java b/paper-api/src/main/java/org/bukkit/UnsafeValues.java
index 56fa266e4..29838175a 100644
--- a/paper-api/src/main/java/org/bukkit/UnsafeValues.java
+++ b/paper-api/src/main/java/org/bukkit/UnsafeValues.java
@@ -126,10 +126,6 @@ public interface UnsafeValues {
@Deprecated(since = "1.20.2", forRemoval = true)
PotionType.InternalPotionData getInternalPotionData(NamespacedKey key);
- @ApiStatus.Internal
- @Nullable
- DamageEffect getDamageEffect(@NotNull String key);
-
/**
* Create a new {@link DamageSource.Builder}.
*
diff --git a/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java b/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java
index 8cf8fde60..2e442f146 100644
--- a/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java
+++ b/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java
@@ -1,7 +1,7 @@
package org.bukkit.damage;
import com.google.common.base.Preconditions;
-import org.bukkit.Bukkit;
+import io.papermc.paper.InternalAPIBridge;
import org.bukkit.Sound;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -40,7 +40,7 @@ public interface DamageEffect {
@NotNull
private static DamageEffect getDamageEffect(@NotNull String key) {
- return Preconditions.checkNotNull(Bukkit.getUnsafe().getDamageEffect(key), "No DamageEffect found for %s. This is a bug.", key);
+ return Preconditions.checkNotNull(InternalAPIBridge.get().getDamageEffect(key), "No DamageEffect found for %s. This is a bug.", key);
}
/**
diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java
new file mode 100644
index 000000000..d3c216f44
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java
@@ -0,0 +1,15 @@
+package io.papermc.paper;
+
+import org.bukkit.craftbukkit.damage.CraftDamageEffect;
+import org.bukkit.damage.DamageEffect;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public class PaperServerInternalAPIBridge implements InternalAPIBridge {
+ public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge();
+
+ @Override
+ public DamageEffect getDamageEffect(final String key) {
+ return CraftDamageEffect.getById(key);
+ }
+}
diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/LaunchEntryPointHandler.java b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/LaunchEntryPointHandler.java
index 48bc745ca..c75f72b47 100644
--- a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/LaunchEntryPointHandler.java
+++ b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/LaunchEntryPointHandler.java
@@ -6,10 +6,9 @@ import io.papermc.paper.plugin.storage.ProviderStorage;
import io.papermc.paper.plugin.storage.ServerPluginProviderStorage;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
-import org.jetbrains.annotations.ApiStatus;
-
import java.util.HashMap;
import java.util.Map;
+import org.jetbrains.annotations.ApiStatus;
/**
* Used by the server to register/load plugin bootstrappers and plugins.
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 c79981e50..35af2f2f7 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
@@ -5,6 +5,7 @@ import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.PaperDataComponentType;
import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry;
+import io.papermc.paper.registry.data.PaperDamageTypeRegistryEntry;
import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
@@ -103,7 +104,7 @@ public final class PaperRegistries {
start(Registries.STRUCTURE, RegistryKey.STRUCTURE).craft(Structure.class, CraftStructure::new).build().delayed(),
start(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL).craft(TrimMaterial.class, CraftTrimMaterial::new).build().delayed(),
start(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN).craft(TrimPattern.class, CraftTrimPattern::new).build().delayed(),
- start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).build().delayed(),
+ start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).writable(PaperDamageTypeRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT).craft(Wolf.Variant.class, CraftWolf.CraftVariant::new).build().delayed(),
start(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT).craft(Enchantment.class, CraftEnchantment::new).serializationUpdater(FieldRename.ENCHANTMENT_RENAME).writable(PaperEnchantmentRegistryEntry.PaperBuilder::new).delayed(),
start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).build().delayed(),
diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDamageTypeRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDamageTypeRegistryEntry.java
new file mode 100644
index 000000000..e28dc4df9
--- /dev/null
+++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDamageTypeRegistryEntry.java
@@ -0,0 +1,112 @@
+package io.papermc.paper.registry.data;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.data.util.Conversions;
+import net.minecraft.world.damagesource.DamageEffects;
+import net.minecraft.world.damagesource.DamageScaling;
+import net.minecraft.world.damagesource.DamageType;
+import net.minecraft.world.damagesource.DeathMessageType;
+import org.bukkit.craftbukkit.damage.CraftDamageEffect;
+import org.bukkit.craftbukkit.damage.CraftDamageType;
+import org.bukkit.damage.DamageEffect;
+import org.jspecify.annotations.Nullable;
+
+import static io.papermc.paper.registry.data.util.Checks.asConfigured;
+
+public class PaperDamageTypeRegistryEntry implements DamageTypeRegistryEntry {
+
+ protected @Nullable String messageId;
+ protected @Nullable Float exhaustion;
+ protected @Nullable DamageScaling damageScaling;
+ protected DamageEffects damageEffects = DamageEffects.HURT;
+ protected DeathMessageType deathMessageType = DeathMessageType.DEFAULT;
+
+ protected final Conversions conversions;
+
+ public PaperDamageTypeRegistryEntry(
+ final Conversions conversions,
+ final @Nullable DamageType internal
+ ) {
+ this.conversions = conversions;
+ if (internal == null) return;
+
+ this.messageId = internal.msgId();
+ this.exhaustion = internal.exhaustion();
+ this.damageScaling = internal.scaling();
+ this.damageEffects = internal.effects();
+ this.deathMessageType = internal.deathMessageType();
+ }
+
+ @Override
+ public String messageId() {
+ return asConfigured(messageId, "messsageId");
+ }
+
+ @Override
+ public float exhaustion() {
+ return asConfigured(exhaustion, "exhaustion");
+ }
+
+ @Override
+ public org.bukkit.damage.DamageScaling damageScaling() {
+ return CraftDamageType.damageScalingToBukkit(asConfigured(this.damageScaling, "damageScaling"));
+ }
+
+ @Override
+ public DamageEffect damageEffect() {
+ return CraftDamageEffect.toBukkit(damageEffects);
+ }
+
+ @Override
+ public org.bukkit.damage.DeathMessageType deathMessageType() {
+ return CraftDamageType.deathMessageTypeToBukkit(deathMessageType);
+ }
+
+ public static final class PaperBuilder extends PaperDamageTypeRegistryEntry implements DamageTypeRegistryEntry.Builder, PaperRegistryBuilder {
+
+ public PaperBuilder(final Conversions conversions, final @Nullable DamageType internal) {
+ super(conversions, internal);
+ }
+
+ @Override
+ public Builder messageId(final String messageId) {
+ this.messageId = messageId;
+ return this;
+ }
+
+ @Override
+ public Builder exhaustion(final float exhaustion) {
+ this.exhaustion = exhaustion;
+ return this;
+ }
+
+ @Override
+ public Builder damageScaling(final org.bukkit.damage.DamageScaling scaling) {
+ this.damageScaling = CraftDamageType.damageScalingToNMS(scaling);
+ return this;
+ }
+
+ @Override
+ public Builder damageEffect(final DamageEffect effect) {
+ this.damageEffects = ((CraftDamageEffect) effect).getHandle();
+ return this;
+ }
+
+ @Override
+ public Builder deathMessageType(final org.bukkit.damage.DeathMessageType deathMessageType) {
+ this.deathMessageType = CraftDamageType.deathMessageTypeToNMS(deathMessageType);
+ return this;
+ }
+
+ @Override
+ public DamageType build() {
+ return new DamageType(
+ asConfigured(this.messageId, "messsageId"),
+ asConfigured(this.damageScaling, "scaling"),
+ asConfigured(this.exhaustion, "exhaustion"),
+ this.damageEffects,
+ this.deathMessageType
+ );
+ }
+ }
+}
diff --git a/paper-server/src/main/resources/META-INF/services/io.papermc.paper.InternalAPIBridge b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.InternalAPIBridge
new file mode 100644
index 000000000..2bae80fd6
--- /dev/null
+++ b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.InternalAPIBridge
@@ -0,0 +1 @@
+io.papermc.paper.PaperServerInternalAPIBridge