diff options
Diffstat (limited to 'bukkit/src')
8 files changed, 129 insertions, 8 deletions
diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java index 00920e77..586ba55d 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java @@ -147,6 +147,7 @@ import com.leonardobishop.quests.bukkit.tasktype.type.dependent.uSkyBlockLevelTa import com.leonardobishop.quests.bukkit.util.CompatUtils; import com.leonardobishop.quests.bukkit.util.FormatUtils; import com.leonardobishop.quests.bukkit.util.LogHistory; +import com.leonardobishop.quests.bukkit.util.Projectile2ItemCache; import com.leonardobishop.quests.common.config.ConfigProblem; import com.leonardobishop.quests.common.config.ConfigProblemDescriptions; import com.leonardobishop.quests.common.config.QuestsConfig; @@ -226,6 +227,7 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests { private QuestsBossBar bossBarHandle; private QuestsActionBar actionBarHandle; private VersionSpecificHandler versionSpecificHandler; + private Projectile2ItemCache projectile2ItemCache; private LogHistory logHistory; private WrappedTask questAutoSaveTask; @@ -370,6 +372,10 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests { }; } + // Instantiate Projectile to ItemStack cache + this.projectile2ItemCache = new Projectile2ItemCache(); + this.projectile2ItemCache.registerEvents(this); + // Set item getter to be used by Quests config this.questsConfig.setItemGetter(this.itemGetter); @@ -972,6 +978,10 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests { return versionSpecificHandler; } + public Projectile2ItemCache getProjectile2ItemCache() { + return projectile2ItemCache; + } + public QuestItemRegistry getQuestItemRegistry() { return questItemRegistry; } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler.java index 08db3e50..dacca889 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler.java @@ -245,10 +245,14 @@ public interface VersionSpecificHandler { /** * {@link DamageSource}s were introduced in {@code 1.20.4}. */ - @SuppressWarnings("UnstableApiUsage") @Nullable Player getDamager(@Nullable EntityDamageEvent lastDamageCause); /** + * {@link DamageSource}s were introduced in {@code 1.20.4}. + */ + @Nullable Entity getDirectSource(@Nullable EntityDamageEvent lastDamageCause); + + /** * {@link Tag#CANDLE_CAKES} was introduced in {@code 1.17}. */ boolean isCake(Material type); diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler20.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler20.java index 5e7065dd..687da6fa 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler20.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler20.java @@ -54,7 +54,6 @@ public class VersionSpecificHandler20 extends VersionSpecificHandler17 implement } } - @SuppressWarnings("UnstableApiUsage") @Override public @Nullable Player getDamager(@Nullable EntityDamageEvent event) { if (!DAMAGE_SOURCE_API) { @@ -74,4 +73,18 @@ public class VersionSpecificHandler20 extends VersionSpecificHandler17 implement return null; } + + @Override + public @Nullable Entity getDirectSource(@Nullable EntityDamageEvent event) { + if (!DAMAGE_SOURCE_API) { + return super.getDamager(event); + } + + if (event == null) { + return null; + } + + DamageSource source = event.getDamageSource(); + return source.getDirectEntity(); + } } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler8.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler8.java index 7e3e2ee4..55ab69ba 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler8.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/versionspecific/VersionSpecificHandler8.java @@ -227,6 +227,11 @@ public class VersionSpecificHandler8 implements VersionSpecificHandler { } @Override + public @Nullable Entity getDirectSource(@Nullable EntityDamageEvent lastDamageCause) { + return null; + } + + @Override public boolean isCake(Material type) { return type == Material.CAKE; } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/DealDamageTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/DealDamageTaskType.java index 469f6c50..be15175d 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/DealDamageTaskType.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/DealDamageTaskType.java @@ -45,9 +45,7 @@ public final class DealDamageTaskType extends BukkitTaskType { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onEntityDamage(EntityDamageEvent event) { - Entity entity = event.getEntity(); Player player = plugin.getVersionSpecificHandler().getDamager(event); - if (player == null || player.hasMetadata("NPC")) { return; } @@ -57,10 +55,15 @@ public final class DealDamageTaskType extends BukkitTaskType { return; } + Entity entity = event.getEntity(); if (!(entity instanceof Damageable damageable)) { return; } + Entity directSource = plugin.getVersionSpecificHandler().getDirectSource(event); + ItemStack bowItem = directSource != null ? plugin.getProjectile2ItemCache().getItem(directSource) : null; + ItemStack item = bowItem != null ? bowItem : plugin.getVersionSpecificHandler().getItemInMainHand(player); + // Clamp entity damage as getDamage() returns Float.MAX_VALUE for killing a parrot with a cookie // https://github.com/LMBishop/Quests/issues/753 double finalDamage = event.getFinalDamage(); @@ -87,7 +90,6 @@ public final class DealDamageTaskType extends BukkitTaskType { } if (task.hasConfigKey("item")) { - ItemStack item = plugin.getVersionSpecificHandler().getItemInMainHand(player); if (item == null) { super.debug("Specific item is required, player has no item in hand; continuing...", quest.getId(), task.getId(), player.getUniqueId()); continue; diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/MobkillingTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/MobkillingTaskType.java index e38cc01f..6b843d89 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/MobkillingTaskType.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/MobkillingTaskType.java @@ -106,6 +106,11 @@ public final class MobkillingTaskType extends BukkitTaskType { return; } + EntityDamageEvent lastDamageCause = entity.getLastDamageCause(); + Entity directSource = plugin.getVersionSpecificHandler().getDirectSource(lastDamageCause); + ItemStack bowItem = directSource != null ? plugin.getProjectile2ItemCache().getItem(directSource) : null; + ItemStack item = bowItem != null ? bowItem : plugin.getVersionSpecificHandler().getItemInMainHand(player); + //noinspection deprecation String customName = entity.getCustomName(); @@ -144,7 +149,6 @@ public final class MobkillingTaskType extends BukkitTaskType { } if (task.hasConfigKey("item")) { - ItemStack item = plugin.getVersionSpecificHandler().getItemInMainHand(player); if (item == null) { super.debug("Specific item is required, player has no item in hand; continuing...", quest.getId(), task.getId(), player.getUniqueId()); continue; diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/MythicMobsDealDamageTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/MythicMobsDealDamageTaskType.java index cdd4578d..f322f856 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/MythicMobsDealDamageTaskType.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/MythicMobsDealDamageTaskType.java @@ -100,7 +100,6 @@ public final class MythicMobsDealDamageTaskType extends BukkitTaskType { } private void handle(final EntityDamageEvent event, final String mobName, final double level) { - Entity entity = event.getEntity(); Player player = plugin.getVersionSpecificHandler().getDamager(event); if (player == null || player.hasMetadata("NPC")) { @@ -112,10 +111,15 @@ public final class MythicMobsDealDamageTaskType extends BukkitTaskType { return; } + Entity entity = event.getEntity(); if (!(entity instanceof Damageable damageable)) { return; } + Entity directSource = plugin.getVersionSpecificHandler().getDirectSource(event); + ItemStack bowItem = directSource != null ? plugin.getProjectile2ItemCache().getItem(directSource) : null; + ItemStack item = bowItem != null ? bowItem : plugin.getVersionSpecificHandler().getItemInMainHand(player); + // Clamp entity damage as getDamage() returns Float.MAX_VALUE for killing a parrot with a cookie // https://github.com/LMBishop/Quests/issues/753 double finalDamage = event.getFinalDamage(); @@ -147,7 +151,6 @@ public final class MythicMobsDealDamageTaskType extends BukkitTaskType { } if (task.hasConfigKey("item")) { - ItemStack item = plugin.getVersionSpecificHandler().getItemInMainHand(player); if (item == null) { super.debug("Specific item is required, player has no item in hand; continuing...", quest.getId(), task.getId(), player.getUniqueId()); continue; diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/Projectile2ItemCache.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/Projectile2ItemCache.java new file mode 100644 index 00000000..1b6b91e0 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/Projectile2ItemCache.java @@ -0,0 +1,80 @@ +package com.leonardobishop.quests.bukkit.util; + +import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent; +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginManager; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Provides a cache that links projectiles to the item used to fire them. + * + * <p>This cache exists because damage-related events do not expose + * information about the item from which a projectile was fired. + * By capturing this association at the time the projectile is created, + * the item can later be retrieved when handling damage events.</p> + */ +@NullMarked +public final class Projectile2ItemCache implements Listener { + + private final Map<Entity, @Nullable ItemStack> backingMap; + + public Projectile2ItemCache() { + this.backingMap = WeakHashMap.newWeakHashMap(1024); + } + + public void registerEvents(final BukkitQuestsPlugin plugin) { + final PluginManager pluginManager = plugin.getServer().getPluginManager(); + + pluginManager.registerEvents(this, plugin); + + try { + Class.forName("com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent"); + pluginManager.registerEvents(new PlayerLaunchProjectileListener(), plugin); + } catch (final ClassNotFoundException e) { + // not supported on Spigot + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityShootBow(final EntityShootBowEvent event) { + final LivingEntity shooter = event.getEntity(); + final Entity projectile = event.getProjectile(); + final ItemStack bow = event.getBow(); + + // Currently there are no advantages of caching projectiles for non-player arrows. + // It would be needed to cache these if we needed a task to take damage from mobs. + if (shooter instanceof Player) { + this.backingMap.put(projectile, bow); + } + } + + public final class PlayerLaunchProjectileListener implements Listener { + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerLaunchProjectile(final PlayerLaunchProjectileEvent event) { + final Projectile projectile = event.getProjectile(); + final ItemStack item = event.getItemStack(); + + // TODO: doesn't really work for tridents + // https://github.com/LMBishop/Quests/pull/833 + Projectile2ItemCache.this.backingMap.put(projectile, item); + } + } + + public @Nullable ItemStack getItem(final Entity projectile) { + return this.backingMap.get(projectile); + } +} |
