Search code examples
javabukkit

Healing/Damage Blocks are outputting twice?


 @EventHandler
    public void playerInteraction(PlayerInteractEvent event)
    {
        Action action = event.getAction();
        Player player = event.getPlayer();
        Block block = event.getClickedBlock();

        if (action.equals(Action.RIGHT_CLICK_BLOCK))
        {
            if (block.getType().equals(Material.NETHER_WART_BLOCK))
            {
                player.setHealth(player.getHealth() -1);
                player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_HURT, 10, 1);
            }
            else if (block.getType().equals(Material.DIAMOND_BLOCK))
            {
                player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 1000, 2));
                player.playSound(player.getLocation(), Sound.ENTITY_SPLASH_POTION_BREAK, 10, 1);
            }
            else if(block.getType().equals(Material.EMERALD_BLOCK))
            {
                if (player.getHealth() != 20)
                {
                    player.setHealth(player.getHealth() + 1);
                    player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 10, 1);;
                }
                if (player.getHealth() == 20)
                {
                    player.sendMessage(ChatColor.DARK_RED + "You are already at full health!");
                }
            }
        }
    }

For some reason, all of these things happen twice whenever I right click the designated blocks. Anyone know why? I have posted the entire method, it's a player interaction event.

Thanks :)


Solution

  • First of all, make sure yo haven't registered the Listener class containing the event handler twice.

    If that's not the case, according to this thread on the spigot forums, since Mojang added the left hand slot to Minecraft some events like PlayerInteractEvent or InventoryClickEvent will be called twice (once for each hand).

    One possible fix is to "disable" the left hand on the event handler:

    @EventHandler
    public void onPlayerInteraction(PlayerInteractEvent event) {
        if(event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getHand() == EquipmentSlot.HAND) {
            //Do something once
        }
    } 
    

    If you require that both hands could be used to trigger the event you could do the following:

    1. First time the code gets executed you add the player to a list.
    2. Before executing the code you check if the player is in the list. If it's in the list it means the code was executed once so you can skip it.
    3. Schedule a task to remove the player from the list some ticks later.

    The code could be as follows:

    public class Foo implements Listener {
    
        //An instance of the main plugin class
        private MainClass plugin;
    
        private List<UUID> playerBlacklist = new ArrayList<>();
    
        @EventHandler
        public void onPlayerInteractEvent(PlayerInteractEvent event) {
            if(playerBlacklist.contains(event.getPlayer().getUniqueId)) {
                return;
            } else {
                blacklistPlayer(event.getPlayer());
            }
            //Do something
        }
    
        private void blacklistPlayer(UUID uuid) {
            playerBlacklist.add(uuid);
            BukkitRunnable runnable = new BukkitRunnable(){
                @Override
                public void run() {
                    playerBlacklist.remove(uuid);
                }
            }
            runnable.runTaskLaterAsynchronously(plugin, 5L);
        }
    }
    

    Let me know if this solved your issue.