From 7bee99714a8e60b870ab8984c456716084563904 Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sun, 16 Feb 2025 20:14:00 +0100
Subject: [PATCH] Cleanup damage source a bit (#12106)

---
 .../java/org/bukkit/damage/DamageSource.java  |   4 +
 .../entity/EntityDamageByBlockEvent.java      |   2 +-
 .../entity/EntityDamageByEntityEvent.java     |   6 +-
 .../0016-Moonrise-optimisation-patches.patch  |   4 +-
 .../ServerGamePacketListenerImpl.java.patch   |   8 +-
 .../damagesource/DamageSource.java.patch      | 146 +++++++-----------
 .../damagesource/DamageSources.java.patch     |  50 ------
 .../world/effect/PoisonMobEffect.java.patch   |   2 +-
 .../minecraft/world/entity/Entity.java.patch  |   4 +-
 .../world/entity/Interaction.java.patch       |   4 +-
 .../world/entity/LivingEntity.java.patch      |   4 +-
 .../world/entity/animal/SnowGolem.java.patch  |   2 +-
 .../world/entity/animal/Turtle.java.patch     |   2 +-
 .../world/entity/player/Player.java.patch     |  10 +-
 .../entity/projectile/EvokerFangs.java.patch  |   2 +-
 .../projectile/ThrownEnderpearl.java.patch    |   2 +-
 .../entity/projectile/WitherSkull.java.patch  |   2 +-
 .../world/level/ServerExplosion.java.patch    |   2 +-
 .../world/level/block/BedBlock.java.patch     |   2 +-
 .../world/level/block/CactusBlock.java.patch  |   2 +-
 .../level/block/CampfireBlock.java.patch      |   2 +-
 .../world/level/block/MagmaBlock.java.patch   |   2 +-
 .../block/PointedDripstoneBlock.java.patch    |   2 +-
 .../level/block/RespawnAnchorBlock.java.patch |   2 +-
 .../block/SweetBerryBushBlock.java.patch      |   2 +-
 .../entity/ConduitBlockEntity.java.patch      |   2 +-
 .../craftbukkit/damage/CraftDamageSource.java |  40 ++---
 .../craftbukkit/damage/CraftDamageType.java   |   2 +-
 .../entity/CraftAbstractArrow.java            |   2 +-
 .../bukkit/craftbukkit/entity/CraftArrow.java |   2 +-
 .../craftbukkit/event/CraftEventFactory.java  |  32 ++--
 31 files changed, 135 insertions(+), 215 deletions(-)
 delete mode 100644 paper-server/patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch

diff --git a/paper-api/src/main/java/org/bukkit/damage/DamageSource.java b/paper-api/src/main/java/org/bukkit/damage/DamageSource.java
index 7635610e56..54fa3ebe54 100644
--- a/paper-api/src/main/java/org/bukkit/damage/DamageSource.java
+++ b/paper-api/src/main/java/org/bukkit/damage/DamageSource.java
@@ -51,6 +51,8 @@ public interface DamageSource {
      * be present if an entity did not cause the damage.
      *
      * @return the location, or null if none
+     * @apiNote the world of the location might be null for positioned-only damage source
+     * not caused by any entity
      */
     @Nullable
     public Location getDamageLocation();
@@ -66,6 +68,8 @@ public interface DamageSource {
      * returned.
      *
      * @return the source of the location or null.
+     * @apiNote the world of the location might be null for positioned-only damage source
+     * not caused by any entity
      */
     @Nullable
     public Location getSourceLocation();
diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
index 971093bd25..35aa2cbc5a 100644
--- a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
+++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
@@ -23,7 +23,7 @@ public class EntityDamageByBlockEvent extends EntityDamageEvent {
 
     @Deprecated(since = "1.20.4", forRemoval = true)
     public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, final double damage) {
-        this(damager, (damager != null) ? damager.getState() : null, damagee, cause, (damager != null) ? DamageSource.builder(DamageType.GENERIC).withDamageLocation(damager.getLocation()).build() : DamageSource.builder(DamageType.GENERIC).build(), damage);
+        this(damager, (damager != null) ? damager.getState() : null, damagee, cause, DamageSource.builder(DamageType.GENERIC).build(), damage);
     }
 
     public EntityDamageByBlockEvent(@Nullable final Block damager, @Nullable final BlockState damagerState, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final DamageSource damageSource, final double damage) {
diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java
index 31a36ed3ba..2a36d5b189 100644
--- a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java
+++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java
@@ -17,7 +17,7 @@ public class EntityDamageByEntityEvent extends EntityDamageEvent {
 
     @Deprecated(since = "1.20.4", forRemoval = true)
     public EntityDamageByEntityEvent(@NotNull final Entity damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, final double damage) {
-        this(damager, damagee, cause, DamageSource.builder(DamageType.GENERIC).withCausingEntity(damager).withDirectEntity(damager).build(), damage);
+        this(damager, damagee, cause, DamageSource.builder(DamageType.GENERIC).build(), damage);
     }
 
     @Deprecated
@@ -29,7 +29,7 @@ public class EntityDamageByEntityEvent extends EntityDamageEvent {
 
     @Deprecated(since = "1.20.4", forRemoval = true)
     public EntityDamageByEntityEvent(@NotNull final Entity damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final Map<DamageModifier, Double> modifiers, @NotNull final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions) {
-        this(damager, damagee, cause, DamageSource.builder(DamageType.GENERIC).withCausingEntity(damager).withDirectEntity(damager).build(), modifiers, modifierFunctions);
+        this(damager, damagee, cause, DamageSource.builder(DamageType.GENERIC).build(), modifiers, modifierFunctions);
     }
 
     @Deprecated
@@ -65,7 +65,7 @@ public class EntityDamageByEntityEvent extends EntityDamageEvent {
      * {@inheritDoc}
      * <p>
      * The {@link DamageSource#getDirectEntity()} may be different from the {@link #getDamager()}
-     * if the Minecraft damage source did not originally include an damager entity, but one was included
+     * if the damage source did not originally include a damager entity, but one was included
      * for this event {@link #getDamager()}.
      */
     @Override
diff --git a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
index 95220d3000..1d2ef43ca7 100644
--- a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
+++ b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
@@ -28372,7 +28372,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
 +    // Paper end - block counting
  }
 diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
-index 449c43286e1483b69667c626b234529a16c19ee1..b268b428ec8f2e50737e1dd5cc705c537322433c 100644
+index 19e4576b4b3be92961e993a8b14c8368789c692e..216482b4bb705520411bdeaa58f6044d05190eb6 100644
 --- a/net/minecraft/world/entity/Entity.java
 +++ b/net/minecraft/world/entity/Entity.java
 @@ -135,7 +135,7 @@ import net.minecraft.world.scores.ScoreHolder;
@@ -30418,7 +30418,7 @@ index 2709803b9266ff4a2034d83321cd0ba4e30fc0aa..26c8c1e5598daf3550aef05b12218c47
      ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk);
  
 diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
-index 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc7ae35984 100644
+index 155bf7ccff5c1332fceda2598342bb03624608ff..c4485f28def66264846a436cfba7bddccb66b82e 100644
 --- a/net/minecraft/world/level/ServerExplosion.java
 +++ b/net/minecraft/world/level/ServerExplosion.java
 @@ -63,6 +63,249 @@ public class ServerExplosion implements Explosion {
diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
index e485e7c214..8589a3b4e7 100644
--- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
+++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch
@@ -1119,7 +1119,13 @@
                          this.player.drop(false);
                      }
  
-@@ -1125,8 +_,34 @@
+@@ -1120,13 +_,39 @@
+ 
+                     return;
+                 case RELEASE_USE_ITEM:
+-                    this.player.releaseUsingItem();
++                    if (this.player.getUseItem() == this.player.getItemInHand(this.player.getUsedItemHand())) this.player.releaseUsingItem(); // Paper - validate use item before processing release
+                     return;
                  case START_DESTROY_BLOCK:
                  case ABORT_DESTROY_BLOCK:
                  case STOP_DESTROY_BLOCK:
diff --git a/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch b/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch
index 00eaac6211..d530c5c2c1 100644
--- a/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch
@@ -1,129 +1,95 @@
 --- a/net/minecraft/world/damagesource/DamageSource.java
 +++ b/net/minecraft/world/damagesource/DamageSource.java
-@@ -20,6 +_,107 @@
+@@ -20,6 +_,92 @@
      private final Entity directEntity;
      @Nullable
      private final Vec3 damageSourcePosition;
 +    // CraftBukkit start
 +    @Nullable
-+    private org.bukkit.block.Block directBlock; // The block that caused the damage. damageSourcePosition is not used for all block damages
++    private org.bukkit.event.entity.EntityDamageEvent.DamageCause knownCause; // When the damage event cause is known by the context of the call rather than the damage source data
 +    @Nullable
-+    private org.bukkit.block.BlockState directBlockState; // The block state of the block relevant to this damage source
-+    private boolean sweep = false;
-+    private boolean melting = false;
-+    private boolean poison = false;
++    private Entity eventEntityDamager = null; // Relevant entity set when the game doesn't normally set a causingEntity/directEntity
 +    @Nullable
-+    private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API
-+
-+    public DamageSource sweep() {
-+        this.sweep = true;
-+        return this;
-+    }
-+
-+    public boolean isSweep() {
-+        return this.sweep;
-+    }
-+
-+    public DamageSource melting() {
-+        this.melting = true;
-+        return this;
-+    }
-+
-+    public boolean isMelting() {
-+        return this.melting;
-+    }
-+
-+    public DamageSource poison() {
-+        this.poison = true;
-+        return this;
-+    }
-+
-+    public boolean isPoison() {
-+        return this.poison;
-+    }
-+
-+    // Paper start - fix DamageSource API
++    private org.bukkit.block.Block eventBlockDamager; // Relevant block set. damageSourcePosition is only used for bad respawn point explosion or custom damage
 +    @Nullable
-+    public Entity getCustomEventDamager() {
-+        return (this.customEventDamager != null) ? this.customEventDamager : this.directEntity;
++    private org.bukkit.block.BlockState fromBlockSnapshot; // Captured block snapshot when the eventBlockDamager is not relevant (e.g. for bad respawn point explosions the block is already removed)
++    private boolean critical; // Supports arrows and sweeping damage
++
++    public DamageSource knownCause(final org.bukkit.event.entity.EntityDamageEvent.DamageCause cause) {
++        final DamageSource damageSource = this.copy();
++        damageSource.knownCause = cause;
++        return damageSource;
 +    }
 +
-+    public DamageSource customEventDamager(Entity entity) {
++    @Nullable
++    public org.bukkit.event.entity.EntityDamageEvent.DamageCause knownCause() {
++        return this.knownCause;
++    }
++
++    @Nullable
++    public Entity eventEntityDamager() {
++        return this.eventEntityDamager;
++    }
++
++    public DamageSource eventEntityDamager(final Entity entity) {
 +        if (this.directEntity != null) {
-+            throw new IllegalStateException("Cannot set custom event damager when direct entity is already set (report a bug to Paper)");
++            throw new IllegalStateException("Cannot set an event damager when a direct entity is already set (report a bug to Paper)");
 +        }
-+        DamageSource damageSource = this.cloneInstance();
-+        damageSource.customEventDamager = entity;
-+        // Paper end - fix DamageSource API
++        final DamageSource damageSource = this.copy();
++        damageSource.eventEntityDamager = entity;
 +        return damageSource;
 +    }
 +
 +    @Nullable
-+    public org.bukkit.block.Block getDirectBlock() {
-+        return this.directBlock;
++    public org.bukkit.block.Block eventBlockDamager() {
++        return this.eventBlockDamager;
 +    }
 +
-+    public DamageSource directBlock(@Nullable net.minecraft.world.level.Level world, @Nullable net.minecraft.core.BlockPos blockPosition) {
-+        if (blockPosition == null || world == null) {
++    public DamageSource eventBlockDamager(final @Nullable net.minecraft.world.level.LevelAccessor level, final @Nullable net.minecraft.core.BlockPos pos) {
++        if (pos == null) {
 +            return this;
 +        }
-+        return this.directBlock(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockPosition));
-+    }
 +
-+    public DamageSource directBlock(@Nullable org.bukkit.block.Block block) {
-+        if (block == null) {
-+            return this;
-+        }
-+        // Cloning the instance lets us return unique instances of DamageSource without affecting constants defined in DamageSources
-+        DamageSource damageSource = this.cloneInstance();
-+        damageSource.directBlock = block;
++        final DamageSource damageSource = this.copy();
++        damageSource.eventBlockDamager = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos);
 +        return damageSource;
 +    }
 +
 +    @Nullable
-+    public org.bukkit.block.BlockState getDirectBlockState() {
-+        return this.directBlockState;
++    public org.bukkit.block.BlockState causingBlockSnapshot() {
++        return this.fromBlockSnapshot;
 +    }
 +
-+    public DamageSource directBlockState(@Nullable org.bukkit.block.BlockState blockState) {
-+        if (blockState == null) {
-+            return this;
++    public DamageSource causingBlockSnapshot(final @Nullable org.bukkit.block.BlockState blockState) {
++        if (this.eventBlockDamager != null) {
++            throw new IllegalStateException("Cannot set a block snapshot when an event block damager is already set (report a bug to Paper)");
 +        }
-+        // Cloning the instance lets us return unique instances of DamageSource without affecting constants defined in DamageSources
-+        DamageSource damageSource = this.cloneInstance();
-+        damageSource.directBlockState = blockState;
++        final DamageSource damageSource = this.copy();
++        damageSource.fromBlockSnapshot = blockState;
 +        return damageSource;
 +    }
 +
-+    private DamageSource cloneInstance() {
-+        DamageSource damageSource = new DamageSource(this.type, this.directEntity, this.causingEntity, this.damageSourcePosition);
-+        damageSource.directBlock = this.getDirectBlock();
-+        damageSource.directBlockState = this.getDirectBlockState();
-+        damageSource.sweep = this.isSweep();
-+        damageSource.poison = this.isPoison();
-+        damageSource.melting = this.isMelting();
++    public boolean isCritical() {
++        return this.critical;
++    }
++
++    public DamageSource critical() {
++        final DamageSource damageSource = this.copy();
++        damageSource.critical = true;
++        return damageSource;
++    }
++
++    // Cloning the instance lets us return unique instances of DamageSource without affecting constants defined in DamageSources
++    private DamageSource copy() {
++        final DamageSource damageSource = new DamageSource(this.type, this.directEntity, this.causingEntity, this.damageSourcePosition);
++        damageSource.knownCause = this.knownCause;
++        damageSource.eventEntityDamager = this.eventEntityDamager;
++        damageSource.eventBlockDamager = this.eventBlockDamager;
++        damageSource.fromBlockSnapshot = this.fromBlockSnapshot;
++        damageSource.critical = this.critical;
 +        return damageSource;
 +    }
 +    // CraftBukkit end
  
      @Override
      public String toString() {
-@@ -134,4 +_,18 @@
-     public Holder<DamageType> typeHolder() {
-         return this.type;
-     }
-+
-+    // Paper start - add critical damage API
-+    private boolean critical;
-+    public boolean isCritical() {
-+        return this.critical;
-+    }
-+    public DamageSource critical() {
-+        return this.critical(true);
-+    }
-+    public DamageSource critical(boolean critical) {
-+        this.critical = critical;
-+        return this;
-+    }
-+    // Paper end - add critical damage API
- }
diff --git a/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch b/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch
deleted file mode 100644
index f13f6629d4..0000000000
--- a/paper-server/patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch
+++ /dev/null
@@ -1,50 +0,0 @@
---- a/net/minecraft/world/damagesource/DamageSources.java
-+++ b/net/minecraft/world/damagesource/DamageSources.java
-@@ -42,9 +_,15 @@
-     private final DamageSource stalagmite;
-     private final DamageSource outsideBorder;
-     private final DamageSource genericKill;
-+    // CraftBukkit start
-+    private final DamageSource melting;
-+    private final DamageSource poison;
- 
-     public DamageSources(RegistryAccess registry) {
-         this.damageTypes = registry.lookupOrThrow(Registries.DAMAGE_TYPE);
-+        this.melting = this.source(DamageTypes.ON_FIRE).melting();
-+        this.poison = this.source(DamageTypes.MAGIC).poison();
-+        // CraftBukkit end
-         this.inFire = this.source(DamageTypes.IN_FIRE);
-         this.campfire = this.source(DamageTypes.CAMPFIRE);
-         this.lightningBolt = this.source(DamageTypes.LIGHTNING_BOLT);
-@@ -84,6 +_,16 @@
-         return new DamageSource(this.damageTypes.getOrThrow(damageTypeKey), causingEntity, directEntity);
-     }
- 
-+    // CraftBukkit start
-+    public DamageSource melting() {
-+        return this.melting;
-+    }
-+
-+    public DamageSource poison() {
-+        return this.poison;
-+    }
-+    // CraftBukkit end
-+
-     public DamageSource inFire() {
-         return this.inFire;
-     }
-@@ -261,7 +_,13 @@
-     }
- 
-     public DamageSource badRespawnPointExplosion(Vec3 position) {
--        return new DamageSource(this.damageTypes.getOrThrow(DamageTypes.BAD_RESPAWN_POINT), position);
-+        // CraftBukkit start
-+        return this.badRespawnPointExplosion(position, null);
-+    }
-+
-+    public DamageSource badRespawnPointExplosion(Vec3 position, org.bukkit.block.BlockState blockState) {
-+        return new DamageSource(this.damageTypes.getOrThrow(DamageTypes.BAD_RESPAWN_POINT), position).directBlockState(blockState);
-+        // CraftBukkit end
-     }
- 
-     public DamageSource outOfBorder() {
diff --git a/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch
index 66061ffa2d..756bcf1d88 100644
--- a/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch
@@ -5,7 +5,7 @@
      public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) {
          if (entity.getHealth() > 1.0F) {
 -            entity.hurtServer(level, entity.damageSources().magic(), 1.0F);
-+            entity.hurtServer(level, entity.damageSources().poison(), 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON
++            entity.hurtServer(level, entity.damageSources().magic().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.POISON), 1.0F); // CraftBukkit
          }
  
          return true;
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch
index 691432fd24..9340b5b32c 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch
@@ -459,7 +459,7 @@
 +            // CraftBukkit end
              if (this.level() instanceof ServerLevel serverLevel
 -                && this.hurtServer(serverLevel, this.damageSources().lava(), 4.0F)
-+                && this.hurtServer(serverLevel, this.damageSources().lava().directBlock(this.level, this.lastLavaContact), 4.0F) // CraftBukkit - we also don't throw an event unless the object in lava is living, to save on some event calls
++                && this.hurtServer(serverLevel, this.damageSources().lava().eventBlockDamager(this.level, this.lastLavaContact), 4.0F) // CraftBukkit - we also don't throw an event unless the object in lava is living, to save on some event calls
                  && this.shouldPlayLavaHurtSound()
                  && !this.isSilent()) {
                  serverLevel.playSound(
@@ -1300,7 +1300,7 @@
 +            return;
 +        }
 +
-+        if (!this.hurtServer(level, this.damageSources().lightningBolt().customEventDamager(lightning), 5.0F)) { // Paper - fix DamageSource API
++        if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightning), 5.0F)) { // Paper - fix DamageSource API
 +            return;
 +        }
 +        // CraftBukkit end
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch
index 3d9857c808..914667d8da 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch
@@ -5,7 +5,7 @@
      public boolean skipAttackInteraction(Entity entity) {
          if (entity instanceof Player player) {
 +            // CraftBukkit start
-+            DamageSource source = player.damageSources().playerAttack(player);
++            DamageSource source = player.damageSources().generic().eventEntityDamager(entity);
 +            org.bukkit.event.entity.EntityDamageEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNonLivingEntityDamageEvent(this, source, 1.0F, false);
 +            if (event.isCancelled()) {
 +                return true;
@@ -14,7 +14,7 @@
              this.attack = new Interaction.PlayerAction(player.getUUID(), this.level().getGameTime());
              if (player instanceof ServerPlayer serverPlayer) {
 -                CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, player.damageSources().generic(), 1.0F, 1.0F, false);
-+                CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, player.damageSources().generic(), 1.0F, (float) event.getFinalDamage(), false); // CraftBukkit // Paper - use correct source and fix taken/dealt param order
++                CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, source, 1.0F, (float) event.getFinalDamage(), false); // CraftBukkit // Paper - use correct source and fix taken/dealt param order
              }
  
              return !this.getResponse();
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
index bca5d0ef6b..8015fbab24 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
@@ -678,7 +678,7 @@
 +            event.setCancelled(itemStack == null);
 +            this.level().getCraftServer().getPluginManager().callEvent(event);
 +            if (event.isCancelled()) {
-+                // Set death protection to null as the event was cancelled. Prevent any attempt at ressurection.
++                // Set death protection to null as the event was cancelled. Prevent any attempt at resurrection.
 +                deathProtection = null;
 +            } else {
 +                if (!itemInHand.isEmpty() && itemStack != null) { // Paper - only reduce item if actual totem was found
@@ -691,7 +691,7 @@
 -                if (this instanceof ServerPlayer serverPlayer) {
 +                }
 +                // Paper start - fix NPE when pre-cancelled EntityResurrectEvent is uncancelled
-+                // restore the previous behavior in that case by defaulting to vanillas totem of undying efect
++                // restore the previous behavior in that case by defaulting to vanillas totem of undying effect
 +                if (deathProtection == null) {
 +                    deathProtection = DeathProtection.TOTEM_OF_UNDYING;
 +                }
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch
index e647b528c6..ab7a9f1255 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch
@@ -5,7 +5,7 @@
          if (this.level() instanceof ServerLevel serverLevel) {
              if (this.level().getBiome(this.blockPosition()).is(BiomeTags.SNOW_GOLEM_MELTS)) {
 -                this.hurtServer(serverLevel, this.damageSources().onFire(), 1.0F);
-+                this.hurtServer(serverLevel, this.damageSources().melting(), 1.0F); // CraftBukkit - DamageSources.ON_FIRE -> CraftEventFactory.MELTING
++                this.hurtServer(serverLevel, this.damageSources().onFire().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.MELTING), 1.0F); // CraftBukkit
              }
  
              if (!serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Turtle.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Turtle.java.patch
index 741323b5b1..608d6617dd 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Turtle.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Turtle.java.patch
@@ -15,7 +15,7 @@
      @Override
      public void thunderHit(ServerLevel level, LightningBolt lightning) {
 -        this.hurtServer(level, this.damageSources().lightningBolt(), Float.MAX_VALUE);
-+        this.hurtServer(level, this.damageSources().lightningBolt().customEventDamager(lightning), Float.MAX_VALUE); // CraftBukkit // Paper - fix DamageSource API
++        this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightning), Float.MAX_VALUE); // CraftBukkit // Paper - fix DamageSource API
      }
  
      @Override
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch
index 5e8dba7c7c..dd2387ae99 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch
@@ -314,7 +314,7 @@
                              && !this.isSprinting();
 +                        flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits
                          if (flag2) {
-+                            damageSource = damageSource.critical(true); // Paper start - critical damage API
++                            damageSource = damageSource.critical(); // Paper - critical damage API
                              f *= 1.5F;
                          }
  
@@ -342,12 +342,14 @@
                              }
  
                              if (flag3) {
-@@ -1212,43 +_,62 @@
+@@ -1212,43 +_,64 @@
                                          && (!(livingEntity2 instanceof ArmorStand) || !((ArmorStand)livingEntity2).isMarker())
                                          && this.distanceToSqr(livingEntity2) < 9.0) {
                                          float f6 = this.getEnchantedDamage(livingEntity2, f5, damageSource) * attackStrengthScale;
-+                                        // CraftBukkit start - Only apply knockback if the damage hits
-+                                        if (!livingEntity2.hurtServer((ServerLevel) this.level(), this.damageSources().playerAttack(this).sweep().critical(flag2), f6)) { // Paper - add critical damage API
++                                        // CraftBukkit start - Only apply knockback if the event is not cancelled
++                                        livingEntity2.lastDamageCancelled = false;
++                                        livingEntity2.hurtServer((ServerLevel) this.level(), damageSource.knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK), f6);
++                                        if (livingEntity2.lastDamageCancelled) {
 +                                            continue;
 +                                        }
 +                                        // CraftBukkit end
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch
index e980aa3858..a28c202eb1 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch
@@ -14,7 +14,7 @@
          if (target.isAlive() && !target.isInvulnerable() && target != owner) {
              if (owner == null) {
 -                target.hurt(this.damageSources().magic(), 6.0F);
-+                target.hurt(this.damageSources().magic().customEventDamager(this), 6.0F); // CraftBukkit // Paper - fix DamageSource API
++                target.hurt(this.damageSources().magic().eventEntityDamager(this), 6.0F); // CraftBukkit // Paper - fix DamageSource API
              } else {
                  if (owner.isAlliedTo(target)) {
                      return;
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch
index 8fdaf1cd3c..10a6491ba8 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch
@@ -40,7 +40,7 @@
                              serverPlayer1.resetFallDistance();
                              serverPlayer1.resetCurrentImpulseContext();
 -                            serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl(), 5.0F);
-+                            serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API
++                            serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API
                          }
  
                          this.playSound(serverLevel, vec3);
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch
index d323b77c2b..2eef3589e7 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch
@@ -10,7 +10,7 @@
                  }
              } else {
 -                flag = var8.hurtServer(serverLevel, this.damageSources().magic(), 5.0F);
-+                flag = var8.hurtServer(serverLevel, this.damageSources().magic().customEventDamager(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls // Paper - fix DamageSource API
++                flag = var8.hurtServer(serverLevel, this.damageSources().magic().eventEntityDamager(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls // Paper - fix DamageSource API
              }
  
              if (flag && var8 instanceof LivingEntity livingEntityx) {
diff --git a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch
index 263bafc98c..a7b3faa86c 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch
@@ -175,7 +175,7 @@
 +            this.yield = event.getYield();
 +        } else {
 +            org.bukkit.block.Block block = location.getBlock();
-+            org.bukkit.block.BlockState blockState = (this.damageSource.getDirectBlockState() != null) ? this.damageSource.getDirectBlockState() : block.getState();
++            org.bukkit.block.BlockState blockState = (this.damageSource.causingBlockSnapshot() != null) ? this.damageSource.causingBlockSnapshot() : block.getState();
 +            BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction());
 +            this.wasCanceled = event.isCancelled();
 +            bukkitBlocks = event.blockList();
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch
index f56e52852d..1b12bd9f77 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch
@@ -59,7 +59,7 @@
 +        }
 +
 +        Vec3 center = pos.getCenter();
-+        level.explode(null, level.damageSources().badRespawnPointExplosion(center, blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
++        level.explode(null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
 +        return InteractionResult.SUCCESS_SERVER;
 +     }
 +    // CraftBukkit end
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch
index af19e96da9..3690ad3d19 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch
@@ -27,7 +27,7 @@
      protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
 -        entity.hurt(level.damageSources().cactus(), 1.0F);
 +        if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
-+        entity.hurt(level.damageSources().cactus().directBlock(level, pos), 1.0F); // CraftBukkit
++        entity.hurt(level.damageSources().cactus().eventBlockDamager(level, pos), 1.0F); // CraftBukkit
      }
  
      @Override
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch
index 0663b2c497..df0ab0b58c 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch
@@ -7,7 +7,7 @@
 +        if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
          if (state.getValue(LIT) && entity instanceof LivingEntity) {
 -            entity.hurt(level.damageSources().campfire(), this.fireDamage);
-+            entity.hurt(level.damageSources().campfire().directBlock(level, pos), (float) this.fireDamage); // CraftBukkit
++            entity.hurt(level.damageSources().campfire().eventBlockDamager(level, pos), (float) this.fireDamage); // CraftBukkit
          }
  
          super.entityInside(state, level, pos, entity);
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch
index ae7d0a7fd6..a3fec4f2d3 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch
@@ -5,7 +5,7 @@
      public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
          if (!entity.isSteppingCarefully() && entity instanceof LivingEntity) {
 -            entity.hurt(level.damageSources().hotFloor(), 1.0F);
-+            entity.hurt(level.damageSources().hotFloor().directBlock(level, pos), 1.0F); // CraftBukkit
++            entity.hurt(level.damageSources().hotFloor().eventBlockDamager(level, pos), 1.0F); // CraftBukkit
          }
  
          super.stepOn(level, pos, state, entity);
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch
index 98e5a6861e..13c6c68459 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch
@@ -17,7 +17,7 @@
      public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
          if (state.getValue(TIP_DIRECTION) == Direction.UP && state.getValue(THICKNESS) == DripstoneThickness.TIP) {
 -            entity.causeFallDamage(fallDistance + 2.0F, 2.0F, level.damageSources().stalagmite());
-+            entity.causeFallDamage(fallDistance + 2.0F, 2.0F, level.damageSources().stalagmite().directBlock(level, pos)); // CraftBukkit
++            entity.causeFallDamage(fallDistance + 2.0F, 2.0F, level.damageSources().stalagmite().eventBlockDamager(level, pos)); // CraftBukkit
          } else {
              super.fallOn(level, state, pos, entity, fallDistance);
          }
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch
index 17ec642eda..c66c883b7c 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch
@@ -31,7 +31,7 @@
          Vec3 center = pos2.getCenter();
          level.explode(
 -            null, level.damageSources().badRespawnPointExplosion(center), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK
-+            null, level.damageSources().badRespawnPointExplosion(center, blockState), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK // CraftBukkit - add state
++            null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK // CraftBukkit - add state
          );
      }
  
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch
index a228885132..ea6d6c6cc3 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch
@@ -24,7 +24,7 @@
                      double abs1 = Math.abs(vec3.z());
                      if (abs >= 0.003F || abs1 >= 0.003F) {
 -                        entity.hurtServer(serverLevel, level.damageSources().sweetBerryBush(), 1.0F);
-+                        entity.hurtServer(serverLevel, level.damageSources().sweetBerryBush().directBlock(level, pos), 1.0F); // CraftBukkit
++                        entity.hurtServer(serverLevel, level.damageSources().sweetBerryBush().eventBlockDamager(level, pos), 1.0F); // CraftBukkit
                      }
                  }
              }
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch
index 31b86a4f4f..226211f28d 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch
@@ -56,7 +56,7 @@
  
 -        if (blockEntity.destroyTarget != null) {
 +        if (damageTarget && blockEntity.destroyTarget != null) { // CraftBukkit
-+            if (blockEntity.destroyTarget.hurtServer((net.minecraft.server.level.ServerLevel) level, level.damageSources().magic().directBlock(level, pos), 4.0F)) // CraftBukkit
++            if (blockEntity.destroyTarget.hurtServer((net.minecraft.server.level.ServerLevel) level, level.damageSources().magic().eventBlockDamager(level, pos), 4.0F)) // CraftBukkit
              level.playSound(
                  null,
                  blockEntity.destroyTarget.getX(),
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java b/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
index 7df86e7124..f2a6c4fdd2 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
@@ -1,10 +1,10 @@
 package org.bukkit.craftbukkit.damage;
 
 import java.util.Objects;
+import net.minecraft.Optionull;
 import net.minecraft.world.phys.Vec3;
 import org.bukkit.Location;
 import org.bukkit.World;
-import org.bukkit.block.Block;
 import org.bukkit.craftbukkit.entity.CraftEntity;
 import org.bukkit.craftbukkit.util.CraftLocation;
 import org.bukkit.damage.DamageSource;
@@ -26,12 +26,7 @@ public class CraftDamageSource implements DamageSource {
     }
 
     public World getCausingEntityWorld() {
-        org.bukkit.entity.Entity causingEntity = this.getCausingEntity();
-        return causingEntity != null ? causingEntity.getWorld() : null;
-    }
-
-    public Block getDirectBlock() {
-        return this.getHandle().getDirectBlock();
+        return Optionull.map(this.getCausingEntity(), Entity::getWorld);
     }
 
     @Override
@@ -41,26 +36,22 @@ public class CraftDamageSource implements DamageSource {
 
     @Override
     public org.bukkit.entity.Entity getCausingEntity() {
-        net.minecraft.world.entity.Entity entity = this.getHandle().getEntity(); // Paper - fix DamageSource API - revert to vanilla
-        return (entity != null) ? entity.getBukkitEntity() : null;
+        return Optionull.map(this.getHandle().getEntity(), net.minecraft.world.entity.Entity::getBukkitEntity);
     }
 
     @Override
     public org.bukkit.entity.Entity getDirectEntity() {
-        net.minecraft.world.entity.Entity entity = this.getHandle().getDirectEntity(); // Paper - fix DamageSource API
-        return (entity != null) ? entity.getBukkitEntity() : null;
+        return Optionull.map(this.getHandle().getDirectEntity(), net.minecraft.world.entity.Entity::getBukkitEntity);
     }
 
     @Override
     public Location getDamageLocation() {
-        Vec3 vec3D = this.getHandle().sourcePositionRaw();
-        return (vec3D != null) ? CraftLocation.toBukkit(vec3D, this.getCausingEntityWorld()) : null;
+        return Optionull.map(this.getHandle().sourcePositionRaw(), sourcePos -> CraftLocation.toBukkit(sourcePos, this.getCausingEntityWorld()));
     }
 
     @Override
     public Location getSourceLocation() {
-        Vec3 vec3D = this.getHandle().getSourcePosition();
-        return (vec3D != null) ? CraftLocation.toBukkit(vec3D, this.getCausingEntityWorld()) : null;
+        return Optionull.map(this.getHandle().getSourcePosition(), sourcePos -> CraftLocation.toBukkit(sourcePos, this.getCausingEntityWorld()));
     }
 
     @Override
@@ -70,7 +61,7 @@ public class CraftDamageSource implements DamageSource {
 
     @Override
     public float getFoodExhaustion() {
-        return this.damageType.getExhaustion();
+        return this.getHandle().getFoodExhaustion();
     }
 
     @Override
@@ -84,28 +75,27 @@ public class CraftDamageSource implements DamageSource {
             return true;
         }
 
-        if (!(obj instanceof DamageSource)) {
+        if (!(obj instanceof DamageSource other)) {
             return false;
         }
 
-        DamageSource other = (DamageSource) obj;
         return Objects.equals(this.getDamageType(), other.getDamageType()) && Objects.equals(this.getCausingEntity(), other.getCausingEntity())
-                && Objects.equals(this.getDirectEntity(), other.getDirectEntity()) && Objects.equals(this.getDamageLocation(), other.getDamageLocation());
+            && Objects.equals(this.getDirectEntity(), other.getDirectEntity()) && Objects.equals(this.getDamageLocation(), other.getDamageLocation());
     }
 
     @Override
     public int hashCode() {
         int result = 1;
         result = 31 * result + this.damageType.hashCode();
-        result = 31 * result + (this.getCausingEntity() != null ? this.getCausingEntity().hashCode() : 0);
-        result = 31 * result + (this.getDirectEntity() != null ? this.getDirectEntity().hashCode() : 0);
-        result = 31 * result + (this.getDamageLocation() != null ? this.getDamageLocation().hashCode() : 0);
+        result = 31 * result + Objects.hashCode(this.getCausingEntity());
+        result = 31 * result + Objects.hashCode(this.getDirectEntity());
+        result = 31 * result + Objects.hashCode(this.getDamageLocation());
         return result;
     }
 
     @Override
     public String toString() {
-        return "DamageSource{damageType=" + this.getDamageType() + ",causingEntity=" + this.getCausingEntity() + ",directEntity=" + this.getDirectEntity() + ",damageLocation=" + this.getDamageLocation() + "}";
+        return "DamageSource{damageType=" + this.getDamageType() + ", causingEntity=" + this.getCausingEntity() + ", directEntity=" + this.getDirectEntity() + ", damageLocation=" + this.getDamageLocation() + "}";
     }
 
     public static DamageSource buildFromBukkit(DamageType damageType, Entity causingEntity, Entity directEntity, Location damageLocation) {
@@ -121,8 +111,8 @@ public class CraftDamageSource implements DamageSource {
             nmsDirectEntity = craftDirectEntity.getHandle();
         }
 
-        Vec3 vec3D = (damageLocation == null) ? null : CraftLocation.toVec3D(damageLocation);
+        Vec3 sourcePos = (damageLocation == null) ? null : CraftLocation.toVec3D(damageLocation);
 
-        return new CraftDamageSource(new net.minecraft.world.damagesource.DamageSource(holderDamageType, nmsDirectEntity, nmsCausingEntity, vec3D));
+        return new CraftDamageSource(new net.minecraft.world.damagesource.DamageSource(holderDamageType, nmsDirectEntity, nmsCausingEntity, sourcePos));
     }
 }
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageType.java
index 3350775859..d907bad23f 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageType.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageType.java
@@ -58,7 +58,7 @@ public class CraftDamageType implements DamageType, Handleable<net.minecraft.wor
 
     @Override
     public String toString() {
-        return "CraftDamageType{" + "key=" + this.getKey() + ",damageScaling=" + this.getDamageScaling() + ",damageEffect=" + this.getDamageEffect() + ",deathMessageType=" + this.getDeathMessageType() + ",exhaustion=" + this.getExhaustion() + "}";
+        return "CraftDamageType{" + "key=" + this.getKey() + ", damageScaling=" + this.getDamageScaling() + ", damageEffect=" + this.getDamageEffect() + ", deathMessageType=" + this.getDeathMessageType() + ", exhaustion=" + this.getExhaustion() + "}";
     }
 
     public static DeathMessageType deathMessageTypeToBukkit(net.minecraft.world.damagesource.DeathMessageType deathMessageType) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
index 66db4132be..e7fe8afd11 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
@@ -147,7 +147,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr
 
     @Override
     public String toString() {
-        return "CraftArrow";
+        return "CraftAbstractArrow";
     }
 
     // Paper start
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
index 15d50a284c..5661d72e70 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
@@ -31,7 +31,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow {
 
     @Override
     public String toString() {
-        return "CraftTippedArrow";
+        return "CraftArrow";
     }
 
     @Override
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index fd4c1d67c1..57c3f8531b 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1092,17 +1092,19 @@ public class CraftEventFactory {
 
     private static EntityDamageEvent handleEntityDamageEvent(Entity entity, DamageSource source, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled) {
         CraftDamageSource bukkitDamageSource = new CraftDamageSource(source);
-        final Entity damager = source.getCustomEventDamager(); // Paper - fix DamageSource API
+        final Entity damager = source.eventEntityDamager() != null ? source.eventEntityDamager() : source.getDirectEntity(); // Paper - fix DamageSource API
         if (source.is(DamageTypeTags.IS_EXPLOSION)) {
             if (damager == null) {
-                return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
+                return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
             }
             DamageCause damageCause = (damager.getBukkitEntity() instanceof org.bukkit.entity.TNTPrimed) ? DamageCause.BLOCK_EXPLOSION : DamageCause.ENTITY_EXPLOSION;
             return CraftEventFactory.callEntityDamageEvent(damager, entity, damageCause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
         } else if (damager != null || source.getDirectEntity() != null) {
-            DamageCause cause = (source.isSweep()) ? DamageCause.ENTITY_SWEEP_ATTACK : DamageCause.ENTITY_ATTACK;
+            DamageCause cause = DamageCause.ENTITY_ATTACK;
 
-            if (damager instanceof net.minecraft.world.entity.projectile.Projectile) {
+            if (source.knownCause() != null) {
+                cause = source.knownCause();
+            } else if (damager instanceof net.minecraft.world.entity.projectile.Projectile) {
                 if (damager.getBukkitEntity() instanceof ThrownPotion) {
                     cause = DamageCause.MAGIC;
                 } else if (damager.getBukkitEntity() instanceof Projectile) {
@@ -1126,12 +1128,14 @@ public class CraftEventFactory {
 
             return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
         } else if (source.is(DamageTypes.FELL_OUT_OF_WORLD)) {
-            return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.VOID, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
+            return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.VOID, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
         } else if (source.is(DamageTypes.LAVA)) {
-            return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
-        } else if (source.getDirectBlock() != null) {
+            return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
+        } else if (source.eventBlockDamager() != null) {
             DamageCause cause;
-            if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) {
+            if (source.knownCause() != null) {
+                cause = source.knownCause();
+            } else if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) {
                 cause = DamageCause.CONTACT;
             } else if (source.is(DamageTypes.HOT_FLOOR)) {
                 cause = DamageCause.HOT_FLOOR;
@@ -1142,13 +1146,15 @@ public class CraftEventFactory {
             } else if (source.is(DamageTypes.CAMPFIRE)) {
                 cause = DamageCause.CAMPFIRE;
             } else {
-                throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s [%s]", entity, source.getDirectBlock(), source.getMsgId(), source.typeHolder().getRegisteredName()));
+                cause = DamageCause.CUSTOM;
             }
-            return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
+            return CraftEventFactory.callEntityDamageEvent(source.eventBlockDamager(), source.causingBlockSnapshot(), entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled);
         }
 
         DamageCause cause;
-        if (source.is(DamageTypes.IN_FIRE)) {
+        if (source.knownCause() != null) {
+            cause = source.knownCause();
+        } else if (source.is(DamageTypes.IN_FIRE)) {
             cause = DamageCause.FIRE;
         } else if (source.is(DamageTypes.STARVE)) {
             cause = DamageCause.STARVATION;
@@ -1160,10 +1166,6 @@ public class CraftEventFactory {
             cause = DamageCause.DROWNING;
         } else if (source.is(DamageTypes.ON_FIRE)) {
             cause = DamageCause.FIRE_TICK;
-        } else if (source.isMelting()) {
-            cause = DamageCause.MELTING;
-        } else if (source.isPoison()) {
-            cause = DamageCause.POISON;
         } else if (source.is(DamageTypes.MAGIC)) {
             cause = DamageCause.MAGIC;
         } else if (source.is(DamageTypes.FALL)) {