How to get the killer of EnderCrystal when player dies from its explosion?

Hi.

Some players have started killing others with ender crystals, so of course this needs to be counted as a PvP statistic. Because of this the event chain that leads to a player death can be quite complicated, as an example:

Player → shoots arrow → it blows up ender crystal → the explosion kills the player
(Or even more complex arrow → crystal → -> explosion → fall-damage)

I’m only doing the simpler one (for now) where the explosion kills the player directly - I think my code should work, but I could use a sanity check from someone more familiar with the Sponge cause API.

What I am unsure of is if event.getCause().allOf(EntityDamageSource.class) returns the different hits the entity has taken, or if it has the chain I described above inside it.

Here is my code so far

    @Listener
    public void onEntityDeath(DestructEntityEvent.Death event) {
        // Get the damage chain that lead to this death
        List<EntityDamageSource> causes = event.getCause().allOf(EntityDamageSource.class);
        Optional<Player> foundKiller = findKiller(causes, 0);
    }

    public Optional<Player> findKiller(List<EntityDamageSource> causes, int depth) {
        if (causes.isEmpty() || causes.size() < depth) {
            return Optional.empty();
        }

        EntityDamageSource damageSource = causes.get(depth);
        Entity killerEntity = damageSource.getSource();

        if (killerEntity instanceof Player) return Optional.of((Player) killerEntity);
        if (killerEntity instanceof Projectile) return getShooter((Projectile) killerEntity);
        // Go one deeper in the cause chain to see what killed the EnderCrystal
        if (killerEntity instanceof EnderCrystal) return findKiller(causes, depth + 1);
        return Optional.empty();
    }

Typically the cause stack holds all information relating to the issue at hand. Therefore if you wanted to get the player that caused the damage (if that be directly, though a projectile, or something else) then just request the root of the cause.

The simplest way to do this is the following

public void onEntityDeath(DamageEntityEvent event, @Root Player killer){
    if(!event.willCauseDeath()){
        return;
    }

Its been a while since I have handled death, i remember it being a pain on Bukkit but I dont have any nightmares of Sponge

That Root Player thing probably solves what I’m trying to accomplish.

Interesting, I didn’t have any issues with finding the player causing of death on bukkit, but I guess I’m looking at it from a different angle in some way.

Now I’m wondering if DamageEntityEvent has the owner of a tamed wolf as the root cause when a wolf kills a player :thinking:

I believe that was the idea of the whole cause stack and everything - I know that in API 7 the cause stack did have some issues so might not work with everything that way.