Implemented EnderCrystal kill detection for Bukkit

Marks player EnderCrystal kills for a player and
also counts mobs killed with EnderCrystal explosions as mob kills for a player.

Affects issues:
- Close #1571
This commit is contained in:
Risto Lahtela 2021-03-21 14:49:14 +02:00
parent 8e0d833f3f
commit 89a3d63d8b

View File

@ -36,7 +36,7 @@ import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.projectiles.ProjectileSource; import org.bukkit.projectiles.ProjectileSource;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.UUID; import java.util.Optional;
/** /**
* Event Listener for EntityDeathEvents. * Event Listener for EntityDeathEvents.
@ -68,36 +68,57 @@ public class DeathEventListener implements Listener {
} }
try { try {
EntityDamageEvent entityDamageEvent = dead.getLastDamageCause(); Optional<Player> foundKiller = findKiller(dead);
if (!(entityDamageEvent instanceof EntityDamageByEntityEvent)) { if (!foundKiller.isPresent()) {
return; return;
} }
Player killer = foundKiller.get();
EntityDamageByEntityEvent entityDamageByEntityEvent = (EntityDamageByEntityEvent) entityDamageEvent; Runnable processor = dead instanceof Player
Entity killerEntity = entityDamageByEntityEvent.getDamager(); ? new PlayerKillProcessor(killer.getUniqueId(), time, dead.getUniqueId(), findWeapon(dead))
: new MobKillProcessor(killer.getUniqueId());
UUID uuid = dead instanceof Player ? dead.getUniqueId() : null; processing.submitCritical(processor);
handleKill(time, uuid, killerEntity);
} catch (Exception e) { } catch (Exception e) {
errorLogger.error(e, ErrorContext.builder().related(event, dead).build()); errorLogger.error(e, ErrorContext.builder().related(event, dead).build());
} }
} }
private void handleKill(long time, UUID victimUUID, Entity killerEntity) { public Optional<Player> findKiller(Entity dead) {
Runnable processor = null; EntityDamageEvent entityDamageEvent = dead.getLastDamageCause();
if (killerEntity instanceof Player) { if (!(entityDamageEvent instanceof EntityDamageByEntityEvent)) {
processor = handlePlayerKill(time, victimUUID, (Player) killerEntity); // Not damaged by entity, can't be a player
} else if (killerEntity instanceof Tameable) { return Optional.empty();
processor = handlePetKill(time, victimUUID, (Tameable) killerEntity);
} else if (killerEntity instanceof Projectile) {
processor = handleProjectileKill(time, victimUUID, (Projectile) killerEntity);
} }
if (processor != null) {
processing.submit(processor); Entity killer = ((EntityDamageByEntityEvent) entityDamageEvent).getDamager();
if (killer instanceof Player) return Optional.of((Player) killer);
if (killer instanceof Tameable) return getOwner((Tameable) killer);
if (killer instanceof Projectile) return getShooter((Projectile) killer);
if (killer instanceof EnderCrystal) return findKiller(killer); // Recursive call
return Optional.empty();
}
public String findWeapon(Entity dead) {
EntityDamageEvent entityDamageEvent = dead.getLastDamageCause();
Entity killer = ((EntityDamageByEntityEvent) entityDamageEvent).getDamager();
if (killer instanceof Player) return getItemInHand((Player) killer);
if (killer instanceof Tameable) return getPetType((Tameable) killer);
// Projectile, EnderCrystal and all other causes that are not known yet
return new EntityNameFormatter().apply(killer.getType().name());
}
private String getPetType(Tameable tameable) {
try {
return tameable.getType().name();
} catch (NoSuchMethodError oldVersionNoTypesError) {
// getType introduced in 1.9
return tameable.getClass().getSimpleName();
} }
} }
private Runnable handlePlayerKill(long time, UUID victimUUID, Player killer) { private String getItemInHand(Player killer) {
Material itemInHand; Material itemInHand;
try { try {
itemInHand = killer.getInventory().getItemInMainHand().getType(); itemInHand = killer.getInventory().getItemInMainHand().getType();
@ -109,48 +130,29 @@ public class DeathEventListener implements Listener {
} }
} }
String weaponName = new ItemNameFormatter().apply(itemInHand.name()); return new ItemNameFormatter().apply(itemInHand.name());
return victimUUID != null
? new PlayerKillProcessor(killer.getUniqueId(), time, victimUUID, weaponName)
: new MobKillProcessor(killer.getUniqueId());
} }
private Runnable handlePetKill(long time, UUID victimUUID, Tameable tameable) { private Optional<Player> getShooter(Projectile projectile) {
ProjectileSource source = projectile.getShooter();
if (source instanceof Player) {
return Optional.of((Player) source);
}
return Optional.empty();
}
private Optional<Player> getOwner(Tameable tameable) {
if (!tameable.isTamed()) { if (!tameable.isTamed()) {
return null; return Optional.empty();
} }
AnimalTamer owner = tameable.getOwner(); AnimalTamer owner = tameable.getOwner();
if (!(owner instanceof Player)) { if (owner instanceof Player) {
return null; return Optional.of((Player) owner);
} }
String name; return Optional.empty();
try {
name = tameable.getType().name();
} catch (NoSuchMethodError oldVersionNoTypesError) {
// getType introduced in 1.9
name = tameable.getClass().getSimpleName();
}
return victimUUID != null
? new PlayerKillProcessor(owner.getUniqueId(), time, victimUUID, new EntityNameFormatter().apply(name))
: new MobKillProcessor(owner.getUniqueId());
}
private Runnable handleProjectileKill(long time, UUID victimUUID, Projectile projectile) {
ProjectileSource source = projectile.getShooter();
if (!(source instanceof Player)) {
return null;
}
Player player = (Player) source;
String projectileName = new EntityNameFormatter().apply(projectile.getType().name());
return victimUUID != null
? new PlayerKillProcessor(player.getUniqueId(), time, victimUUID, projectileName)
: new MobKillProcessor(player.getUniqueId());
} }
} }