Plan/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/DeathEventListener.java

173 lines
6.4 KiB
Java

/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.listeners.bukkit;
import com.djrapitops.plan.delivery.formatting.EntityNameFormatter;
import com.djrapitops.plan.delivery.formatting.ItemNameFormatter;
import com.djrapitops.plan.gathering.cache.SessionCache;
import com.djrapitops.plan.gathering.domain.ActiveSession;
import com.djrapitops.plan.gathering.domain.PlayerKill;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import org.bukkit.Material;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.projectiles.ProjectileSource;
import javax.inject.Inject;
import java.util.Optional;
/**
* Event Listener for EntityDeathEvents.
*
* @author AuroraLS3
*/
public class DeathEventListener implements Listener {
private final ServerInfo serverInfo;
private final Processing processing;
private final ErrorLogger errorLogger;
@Inject
public DeathEventListener(
ServerInfo serverInfo,
Processing processing,
ErrorLogger errorLogger
) {
this.serverInfo = serverInfo;
this.processing = processing;
this.errorLogger = errorLogger;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onDeath(EntityDeathEvent event) {
long time = System.currentTimeMillis();
LivingEntity dead = event.getEntity();
if (dead instanceof Player) {
// Process Death
SessionCache.getCachedSession(dead.getUniqueId()).ifPresent(ActiveSession::addDeath);
}
try {
Optional<Player> foundKiller = findKiller(dead);
if (foundKiller.isEmpty()) {
return;
}
Player killer = foundKiller.get();
Runnable processor = dead instanceof Player
? new PlayerKillProcessor(getKiller(killer), getVictim((Player) dead), serverInfo.getServerIdentifier(), findWeapon(dead), time)
: new MobKillProcessor(killer.getUniqueId());
processing.submitCritical(processor);
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder().related(event, dead).build());
}
}
private PlayerKill.Killer getKiller(Player killer) {
return new PlayerKill.Killer(killer.getUniqueId(), killer.getName());
}
private PlayerKill.Victim getVictim(Player victim) {
return new PlayerKill.Victim(victim.getUniqueId(), victim.getName());
}
public Optional<Player> findKiller(Entity dead) {
EntityDamageEvent entityDamageEvent = dead.getLastDamageCause();
if (!(entityDamageEvent instanceof EntityDamageByEntityEvent)) {
// Not damaged by entity, can't be a player
return Optional.empty();
}
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();
if (entityDamageEvent == null) return "Unknown (No damage cause defined)";
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 String getItemInHand(Player killer) {
Material itemInHand;
try {
itemInHand = killer.getInventory().getItemInMainHand().getType();
} catch (NoSuchMethodError oldVersion) {
try {
itemInHand = killer.getInventory().getItemInHand().getType(); // Support for non dual wielding versions.
} catch (Exception | NoSuchMethodError | NoSuchFieldError unknownError) {
itemInHand = Material.AIR;
}
}
return new ItemNameFormatter().apply(itemInHand.name());
}
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()) {
return Optional.empty();
}
AnimalTamer owner = tameable.getOwner();
if (owner instanceof Player) {
return Optional.of((Player) owner);
}
return Optional.empty();
}
}