diff --git a/build.gradle.kts b/build.gradle.kts index 0135d643731..e2b8dda8c2f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -96,6 +96,8 @@ tasks { // Backwards compatible jar directory destinationDirectory.set(file("$projectDir/target")) archiveClassifier.set("") + + exclude("javax/annotation/**") } runShadow { diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 8b4986cba4a..755d8bd002b 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -13,12 +13,9 @@ import cn.nukkit.entity.item.*; import cn.nukkit.entity.projectile.EntityArrow; import cn.nukkit.entity.projectile.EntityThrownTrident; -import cn.nukkit.event.entity.EntityDamageByBlockEvent; -import cn.nukkit.event.entity.EntityDamageByEntityEvent; -import cn.nukkit.event.entity.EntityDamageEvent; +import cn.nukkit.event.entity.*; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.entity.EntityDamageEvent.DamageModifier; -import cn.nukkit.event.entity.ProjectileLaunchEvent; import cn.nukkit.event.inventory.InventoryCloseEvent; import cn.nukkit.event.inventory.InventoryPickupArrowEvent; import cn.nukkit.event.inventory.InventoryPickupItemEvent; @@ -4340,7 +4337,7 @@ protected void respawn() { this.deadTicks = 0; this.noDamageTicks = 60; - this.removeAllEffects(); + this.removeAllEffects(EntityPotionEffectEvent.Cause.DEATH); this.setHealth(this.getMaxHealth()); this.getFoodData().setLevel(20, 20); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java index f84df308af3..03e9d704154 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; import cn.nukkit.block.BlockID; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.inventory.BeaconInventory; import cn.nukkit.item.ItemBlock; import cn.nukkit.level.format.FullChunk; @@ -121,7 +122,7 @@ public boolean onUpdate() { e.setVisible(false); //Add the effect - p.addEffect(e); + p.addEffect(e, EntityPotionEffectEvent.Cause.BEACON); } //If we have a secondary power as regen, apply it @@ -139,7 +140,7 @@ public boolean onUpdate() { e.setVisible(false); //Add effect - p.addEffect(e); + p.addEffect(e, EntityPotionEffectEvent.Cause.BEACON); } } } diff --git a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java index d60f51d49a2..f7b3f68b494 100644 --- a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java @@ -6,6 +6,7 @@ import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.potion.Effect; import cn.nukkit.potion.InstantEffect; @@ -62,9 +63,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) return true; } if (args[1].equalsIgnoreCase("clear")) { - for (Effect effect : player.getEffects().values()) { - player.removeEffect(effect.getId()); - } + player.removeAllEffects(EntityPotionEffectEvent.Cause.COMMAND); sender.sendMessage(new TranslationContainer("commands.effect.success.removed.all", player.getDisplayName())); return true; } @@ -117,11 +116,11 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) } return true; } - player.removeEffect(effect.getId()); + player.removeEffect(effect.getId(), EntityPotionEffectEvent.Cause.COMMAND); sender.sendMessage(new TranslationContainer("commands.effect.success.removed", effect.getName(), player.getDisplayName())); } else { effect.setDuration(duration).setAmplifier(amplification); - player.addEffect(effect); + player.addEffect(effect, EntityPotionEffectEvent.Cause.COMMAND); Command.broadcastCommandMessage(sender, new TranslationContainer("%commands.effect.success", effect.getName(), String.valueOf(effect.getAmplifier()), player.getDisplayName(), String.valueOf(effect.getDuration() / 20))); } return true; diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index 5b8dd04de39..475376b7205 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -467,7 +467,7 @@ protected void initEntity() { effect.setAmplifier(e.getByte("Amplifier")).setDuration(e.getInt("Duration")).setVisible(e.getBoolean("ShowParticles")); - this.addEffect(effect); + this.addEffect(effect, null); // No event } } @@ -743,15 +743,35 @@ public Map getEffects() { } public void removeAllEffects() { + this.removeAllEffects(EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public void removeAllEffects(EntityPotionEffectEvent.Cause cause) { for (Effect effect : this.effects.values()) { - this.removeEffect(effect.getId()); + this.removeEffect(effect.getId(), cause); } } public void removeEffect(int effectId) { + this.removeEffect(effectId, EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public void removeEffect(int effectId, EntityPotionEffectEvent.Cause cause) { if (this.effects.containsKey(effectId)) { Effect effect = this.effects.get(effectId); + + if (cause != null) { + EntityPotionEffectEvent event = + new EntityPotionEffectEvent(this, effect, null, EntityPotionEffectEvent.Action.REMOVED, cause); + + this.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + this.effects.remove(effectId); + effect.remove(this); this.recalculateEffectColor(); @@ -767,8 +787,28 @@ public boolean hasEffect(int effectId) { } public void addEffect(Effect effect) { + this.addEffect(effect, EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public void addEffect(Effect effect, EntityPotionEffectEvent.Cause cause) { if (effect == null) { - return; //here add null means add nothing + return; + } + + if (cause != null) { + Effect oldEffect = this.effects.get(effect.getId()); + + EntityPotionEffectEvent event = new EntityPotionEffectEvent( + this, + oldEffect, + effect, + oldEffect == null ? EntityPotionEffectEvent.Action.ADDED : EntityPotionEffectEvent.Action.CHANGED, + cause); + + this.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } } effect.add(this); @@ -1150,12 +1190,12 @@ public boolean attack(EntityDamageEvent source) { this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_TOTEM); this.extinguish(); - this.removeAllEffects(); + this.removeAllEffects(EntityPotionEffectEvent.Cause.TOTEM); this.setHealth(1); - this.addEffect(Effect.getEffect(Effect.REGENERATION).setDuration(800).setAmplifier(1)); - this.addEffect(Effect.getEffect(Effect.FIRE_RESISTANCE).setDuration(800)); - this.addEffect(Effect.getEffect(Effect.ABSORPTION).setDuration(100).setAmplifier(1)); + this.addEffect(Effect.getEffect(Effect.REGENERATION).setDuration(800).setAmplifier(1), EntityPotionEffectEvent.Cause.TOTEM); + this.addEffect(Effect.getEffect(Effect.FIRE_RESISTANCE).setDuration(800), EntityPotionEffectEvent.Cause.TOTEM); + this.addEffect(Effect.getEffect(Effect.ABSORPTION).setDuration(100).setAmplifier(1), EntityPotionEffectEvent.Cause.TOTEM); EntityEventPacket pk = new EntityEventPacket(); pk.eid = this.getId(); @@ -1353,7 +1393,7 @@ public boolean entityBaseTick(int tickDiff) { this.justCreated = false; if (!this.isAlive()) { - this.removeAllEffects(); + this.removeAllEffects(EntityPotionEffectEvent.Cause.DEATH); this.despawnFromAll(); if (!this.isPlayer) { this.close(); @@ -1375,7 +1415,7 @@ public boolean entityBaseTick(int tickDiff) { effect.setDuration(effect.getDuration() - tickDiff); if (effect.getDuration() <= 0) { - this.removeEffect(effect.getId()); + this.removeEffect(effect.getId(), EntityPotionEffectEvent.Cause.EXPIRATION); } } } diff --git a/src/main/java/cn/nukkit/event/entity/EntityPotionEffectEvent.java b/src/main/java/cn/nukkit/event/entity/EntityPotionEffectEvent.java new file mode 100644 index 00000000000..70029a1e906 --- /dev/null +++ b/src/main/java/cn/nukkit/event/entity/EntityPotionEffectEvent.java @@ -0,0 +1,183 @@ +package cn.nukkit.event.entity; + +import cn.nukkit.entity.Entity; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.potion.Effect; +import lombok.Getter; + +import javax.annotation.Nullable; + +/** + * Called when a potion effect is modified on an entity. + * If the event is cancelled, no change will be made on the entity. + */ +public class EntityPotionEffectEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + /** + * Gets the old potion effect of the changed type, which will be removed. Null if Action == ADDED. + */ + @Getter + @Nullable + private final Effect oldEffect; + /** + * Gets new potion effect of the changed type to be applied. Null if Action == REMOVED. + */ + @Getter + @Nullable + private final Effect newEffect; + /** + * Gets the action which will be performed on the potion effect type. + */ + @Getter + private final Action action; + /** + * Gets the cause why the effect has changed. + */ + @Getter + private final Cause cause; + + public EntityPotionEffectEvent(Entity entity, Effect oldEffect, Effect newEffect, Action action, Cause cause) { + this.entity = entity; + this.oldEffect = oldEffect; + this.newEffect = newEffect; + this.action = action; + this.cause = cause; + } + + /** + * An enum to specify the action to be performed. + */ + public enum Action { + + /** + * When the potion effect is added because the entity didn't have it's type. + */ + ADDED, + /** + * When the entity already had the potion effect type, but the effect is changed. + */ + CHANGED, + /** + * When the potion effect type is completely removed. + */ + REMOVED + } + + /** + * An enum to specify the cause why an effect was changed. + */ + public enum Cause { + + /** + * When the entity stands inside an area effect cloud. + */ + AREA_EFFECT_CLOUD, + /** + * When the entity is hit by an spectral or tipped arrow. + */ + ARROW, + /** + * When the entity is inflicted with a potion effect due to an entity + * attack (e.g. a cave spider or a shulker bullet). + */ + ATTACK, + /** + * When an entity gets the effect from an axolotl. + */ + AXOLOTL, + /** + * When beacon effects get applied due to the entity being nearby. + */ + BEACON, + /** + * When a potion effect is changed due to the /effect command. + */ + COMMAND, + /** + * When the entity gets the effect from a conduit. + */ + CONDUIT, + /** + * When a conversion from a villager zombie to a villager is started or + * finished. + */ + CONVERSION, + /** + * When all effects are removed due to death (Note: This is called on + * respawn, so it's player only!) + */ + DEATH, + /** + * When the entity gets the effect from a dolphin. + */ + DOLPHIN, + /** + * When the effect was removed due to expiration. + */ + EXPIRATION, + /** + * When an effect is inflicted due to food (e.g. when a player eats or a + * cookie is given to a parrot). + */ + FOOD, + /** + * When all effects are removed due to a bucket of milk. + */ + MILK, + /** + * When a player gets bad omen after killing a patrol captain. + */ + PATROL_CAPTAIN, + /** + * When a potion effect is modified through the plugin methods. + */ + PLUGIN, + /** + * When the entity drinks a potion. + */ + POTION_DRINK, + /** + * When the entity is inflicted with an effect due to a splash potion. + */ + POTION_SPLASH, + /** + * When a spider gets effects when spawning on hard difficulty. + */ + SPIDER_SPAWN, + /** + * When the entity gets effects from a totem item saving it's life. + */ + TOTEM, + /** + * When the entity gets water breathing by wearing a turtle helmet. + */ + TURTLE_HELMET, + /** + * When the Cause is missing. + */ + UNKNOWN, + /** + * When a villager gets regeneration after a trade. + */ + VILLAGER_TRADE, + /** + * When an entity gets the effect from a warden. + */ + WARDEN, + /** + * When an entity comes in contact with a wither rose. + */ + WITHER_ROSE, + /** + * When nearby elder guardian gives mining fatigue to player. + */ + ELDER_GUARDIAN + } +} diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index baf7eb4c70c..8b8740ef62c 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.block.*; import cn.nukkit.entity.Entity; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.event.player.PlayerBucketEmptyEvent; import cn.nukkit.event.player.PlayerBucketFillEvent; import cn.nukkit.event.player.PlayerItemConsumeEvent; @@ -244,7 +245,7 @@ public boolean onUse(Player player, int ticksUsed) { player.getInventory().setItemInHand(Item.get(Item.BUCKET)); } - player.removeAllEffects(); + player.removeAllEffects(EntityPotionEffectEvent.Cause.MILK); return true; } } diff --git a/src/main/java/cn/nukkit/item/ItemHoneyBottle.java b/src/main/java/cn/nukkit/item/ItemHoneyBottle.java index bfb42f82a43..d365970b83e 100644 --- a/src/main/java/cn/nukkit/item/ItemHoneyBottle.java +++ b/src/main/java/cn/nukkit/item/ItemHoneyBottle.java @@ -1,6 +1,7 @@ package cn.nukkit.item; import cn.nukkit.Player; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.potion.Effect; /** @@ -31,7 +32,7 @@ public boolean onUse(Player player, int ticksUsed) { super.onUse(player, ticksUsed); if (player.hasEffect(Effect.POISON)) { - player.removeEffect(Effect.POISON); + player.removeEffect(Effect.POISON, EntityPotionEffectEvent.Cause.FOOD); } player.getInventory().setItemInHand(this); diff --git a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java index 3966e261d23..57d08fa2a5d 100644 --- a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java +++ b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java @@ -2,6 +2,7 @@ import cn.nukkit.entity.Entity; import cn.nukkit.entity.EntityArthropod; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.potion.Effect; import java.util.concurrent.ThreadLocalRandom; @@ -39,7 +40,9 @@ public double getDamageBonus(Entity entity) { public void doPostAttack(Entity attacker, Entity entity) { if (entity instanceof EntityArthropod) { int duration = 20 + ThreadLocalRandom.current().nextInt(10 * this.level); - entity.addEffect(Effect.getEffect(Effect.SLOWNESS).setDuration(duration).setAmplifier(3)); + entity.addEffect( + Effect.getEffect(Effect.SLOWNESS).setDuration(duration).setAmplifier(3), + EntityPotionEffectEvent.Cause.ATTACK); } } } diff --git a/src/main/java/cn/nukkit/item/food/FoodEffective.java b/src/main/java/cn/nukkit/item/food/FoodEffective.java index 612c2e7390d..0f655a8c718 100644 --- a/src/main/java/cn/nukkit/item/food/FoodEffective.java +++ b/src/main/java/cn/nukkit/item/food/FoodEffective.java @@ -1,11 +1,10 @@ package cn.nukkit.item.food; import cn.nukkit.Player; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.potion.Effect; import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; /** @@ -35,11 +34,11 @@ public FoodEffective addChanceEffect(float chance, Effect effect) { @Override protected boolean onEatenBy(Player player) { super.onEatenBy(player); - List toApply = new LinkedList<>(); effects.forEach((effect, chance) -> { - if (chance >= Math.random()) toApply.add(effect.clone()); + if (chance >= Math.random()) { + player.addEffect(effect.clone(), EntityPotionEffectEvent.Cause.FOOD); + } }); - toApply.forEach(player::addEffect); return true; } } diff --git a/src/main/java/cn/nukkit/item/food/FoodMilk.java b/src/main/java/cn/nukkit/item/food/FoodMilk.java index 2d2cd7c3ec9..3c1e765a9b1 100644 --- a/src/main/java/cn/nukkit/item/food/FoodMilk.java +++ b/src/main/java/cn/nukkit/item/food/FoodMilk.java @@ -1,6 +1,7 @@ package cn.nukkit.item.food; import cn.nukkit.Player; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.item.ItemBucket; /** @@ -12,7 +13,7 @@ public class FoodMilk extends Food { protected boolean onEatenBy(Player player) { super.onEatenBy(player); player.getInventory().addItem(new ItemBucket()); - player.removeAllEffects(); + player.removeAllEffects(EntityPotionEffectEvent.Cause.MILK); return true; } } diff --git a/src/main/java/cn/nukkit/potion/Potion.java b/src/main/java/cn/nukkit/potion/Potion.java index 5218df2e1b4..dc4e837e95f 100644 --- a/src/main/java/cn/nukkit/potion/Potion.java +++ b/src/main/java/cn/nukkit/potion/Potion.java @@ -6,6 +6,7 @@ import cn.nukkit.entity.EntitySmite; import cn.nukkit.event.entity.EntityDamageEvent; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; +import cn.nukkit.event.entity.EntityPotionEffectEvent; import cn.nukkit.event.entity.EntityRegainHealthEvent; import cn.nukkit.event.potion.PotionApplyEvent; import cn.nukkit.utils.ServerException; @@ -219,7 +220,7 @@ public void applyPotion(Entity entity, double health) { default: int duration = (int) ((isSplash() ? health : 1) * (double) applyEffect.getDuration() + 0.5); applyEffect.setDuration(duration); - entity.addEffect(applyEffect); + entity.addEffect(applyEffect, this.splash ? EntityPotionEffectEvent.Cause.POTION_SPLASH : EntityPotionEffectEvent.Cause.POTION_DRINK); } }