Solved How Can I Make a PlayerVoidDeath Event

Discussion in 'Spigot Plugin Development' started by Poofyjerry, Mar 27, 2020.

  1. I need to accomplish 3 things
    -Get the killer who knocked the victim into the void if there is one
    -Increment the killers Statistic.PLAYER_KILLS by 1
    -Increment the victims Statistic.DEATHS by 1

    I know that there is EntityDamageEvent which you can use .getCause() on and see if it is void or not... I also know that there is a PlayerDeathEvent which you can get when a player dies and who the killer is.
    So I'm not really sure if there is some makeshift way I can come up with an event for when a player dies by void use the EntityDamage or PlayerDeath event. Or maybe an entirely different event.

    Here is a very brief pseudocode of what i want:
    Code (Text):

      public void onVoidDeath(PlayerVoidDeathEvent e) {
        Increment kills by 1 for e.getKiller();
        Increment deaths by 1 for e.getVictim();
      }
     
    I also am aware that there isn't really a way to see what player knocked someone into void (or maybe there is because sometimes minecraft counts it??), so maybe it would be okay if the killer counted as the last player to hit the victim.

    Or more complicated a pseudocode for ensuring that a player knocked them off that probably wont work:
    1) event for everytime a player is harmed
    2) if the player harmed from that point on and does not make contact with block continue
    3) then if the player is damaged (or killed) by void and still has not made contact with block continue
    4) increment kill for player to last hit victim and increment victims death count

    ANY HELP WOULD BE APPRECIATED
    THANK YOU!!
     
  2. This is sufficient.
    Code (Text):
    @EventHandler
    public void onDeath(PlayerDeathEvent e) {
        Entity entity = e.getEntity();
        iif(entity.getType() == EntityType.PLAYER && e.getDeathMessage().contains("fell out of the world")) { // Check if void killed the player using the Death Message.
            // Player Exists
            Player player = (Player) entity;
            player.incrementStatistic(Statistic.DEATHS);
            Player killer = player.getKiller();
            if(killer != null) {
                killer.incrementStatistic(Statistic.PLAYER_KILLS);
            }
        }
    }
    Make sure the class with the method above `extends Listener` and the event is registered. Presuming the event and method are in your main class, you can do so like this:
    Code (Text):
    this.getServer().getPluginManager().registerEvents(this, this);
    Please see my implementation in a reply below for a more accurate solution.
     
    #2 View, Mar 27, 2020
    Last edited: Mar 28, 2020
  3. That is not sufficient as it never checks for if the death happened in the void?

    Anyway there is a default Minecraft check for if someone died by the void by another player but it's a very short check so what I recommend is putting the damager and victim in like a hashmap on entitydamagebyentityevent and then also save the System.CurrentTimeMillis() and the damager in another hashmap so you can make the check longer, if it this is hard to understand I could try and explain using my system since that's what I do for void kills.
     
  4. In order to check whether or not the void killed the player, simply check the death message. Replace the fourth line on my original post with
    Code (Text):
    if(entity.getType() == EntityType.PLAYER && e.getDeathMessage().contains("fell out of the world")) {
    and you should be good to go.
    Please see my implementation in a reply below for a more accurate solution.
     
    #4 View, Mar 28, 2020
    Last edited: Mar 28, 2020
  5. If you also read his post you could see that he needs the void check that is defaultly in Minecraft to be longer then it is right now so that would work possibly 50% of the time. Also I would not recommend personally using PlayerDeathEvent and instead EntityDeathEvent and check for if event.getCause() == DamageCause.VOID instead of checking if the death message has similar words of the same as the death message would. Not to mention I don't think that's the correct format for the player killed player by void message that's only correct if the player suicided in the void on their own.
     
  6. electronicboy

    IRC Staff

    Death has a few quirks,
    getKiller will store the last player to damage them for like 5 seconds from what I recall, or something like that, you would check their actual death cause by getting the last damage event, conveniently also stored on the Player, you'd need to ref the javadocs for that one
     
    • Agree Agree x 1
  7. getCause() is not a valid method that is available from PlayerDeathEvent.
    Please see the wiki: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/PlayerDeathEvent.html
     
  8. Which is why I said use EntityDeathEvent instead right before that, if you bothered to read instead of just glance.

    Edit: actually it's not e.getCause() it's p.getLastDamageCause() would be the same thing essentially though in checking for a void damage cause and I still recommend EntityDeathEvent.
     
  9. Ah, my bad. Okay, here's my new implementation:
    Code (Text):
    private HashMap<Player, Player> lastDamager = new HashMap<Player, Player>();
    @EventHandler
    public void onPlayerDamage(EntityDamageByEntityEvent e) {
        Entity localEntity = e.getEntity();
        Entity damagerEntity = e.getDamager();
        if(localEntity.getType() == EntityType.PLAYER && damagerEntity.getType() == EntityType.PLAYER) {
            Player localPlayer = (Player) localEntity;
            Player damagerPlayer = (Player) damagerEntity;
            lastDamager.put(localPlayer, damagerPlayer);
        }
    }
    @EventHandler
    public void onDamage(EntityDamageEvent e) {
        Entity entity = e.getEntity();
        if(entity.getType() == EntityType.PLAYER) {
            Player player = (Player) entity;
            if(e.getCause() == DamageCause.VOID && player.isDead() && lastDamager.containsKey(player)) {
                Player killer = lastDamager.get(player);
                player.incrementStatistic(Statistic.DEATHS);
                killer.incrementStatistic(Statistic.PLAYER_KILLS);
                lastDamager.remove(player);
            }
        }
    }
     
  10. Looks nice, the only problem with this is I believe whenever a player dies of void the killer is always null.. I could be wrong but thats what I got when I tried
     
  11. Please try the code I just posted. That solution should be more accurate. Apologies for my misstep.
     
  12. That's only half true it's null because of as @electronicboy said it's like 5 seconds on how long getKiller is saved for. If you tried closer to the void and maybe on low health you'd surely get it near close to Everytime I would think.
     

  13. This worked for me besides the check for if the player.isDead(). I found that you dont need to do that because the last damager would be void so the code in the if statement should only run once because the last damager would only be the player one time. Unless players got down to above the void in my map and somehow boosted their kills there taking advantage of jumping in and out of the void I should be fine.

    Thank You!!
     
    • Friendly Friendly x 1
  14. My pleasure. Glad the solution worked out. You may want to mark this thread as solved.
     
  15. That solution is ok, but I see a few problems. Here's my version, commented with my improvements:

    Code (Java):
    private Map<Player, Player> lastDamager = new HashMap<>(); // uses substitution principle (e.g. Map instead of HashMap)

    @EventHandler
    public void onDamage(EntityDamageByEntityEvent event) {
        Entity victim = event.getEntity();
        Entiy damager = event.getDamager();
            if (victim instanceof Player && damager instanceof  Player) { // safer checks
                lastDamager.put((Player)victim, (Player)damager); // cast straight away, no need to assign variables
            }
    }

    @EventHandler
    public void onDeath(PlayerDeathEvent event) { // direct access to a player death
        Player victim = event.getPlayer();
        EntityDamageEvent dmgEvent = victim.getLastDamageEvent(); // not sure if this is the exact method name
        if (dmgEvent != null && dmgEvent.getCause() == EntityDamageEvent.DamageCause.VOID) {
            // Increment victim's deaths however you like
            if (lastDamager.containsKey(victim)) {
                Player killer = lastDamager.get(victim);
                // Increment killer's deaths however you like
                // An issue the last response had was if you fell into the void with no last damager, your deaths wouldn't go up
                lastDamager.remove(victim);
            }
        }
    }

    @EventHandler
    public void onQuit(PlayerQuitEvent event) {
        lastDamager.remove(event.getPlayer()); // REMOVE REFERENCES TO PLAYERS WHEN THEY LEAVE!!! To avoid memory leaks.
    }