1.15.2 What's the best way to do this with a BukkitRunnable?

Discussion in 'Spigot Plugin Development' started by VivianMusic, Mar 26, 2020.

  1. Hello, I am currently trying to make it to where one of my classes will have something activated when they aren't attached for 5 seconds. Here's what I have

    Code (Text):

    @EventHandler
    public void onDamage(EntityDamageEvent event) {
        Entity ent = event.getEntity();

        if(ent instanceof Player) {
            Player player = (Player) event.getEntity();
            CPlayer cPlayer = ClassesMain.getInstance().cPlayerManager.getCPlayerByUUID(player.getUniqueId());

            if(cPlayer != null) {
                if(cPlayer.getClassType().equals(ClassType.WIZARD)) {
                    if(!(cPlayer.isInPassiveAvoidance())) {

                        cPlayer.setInPassiveAvoidance(true);

                        new BukkitRunnable() {
                            public void run() {
                                if(cPlayer.isInPassiveAvoidance()) {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));
                                } else {
                                    cancel();
                                }
                            }
                        }.runTaskLater(ClassesMain.getInstance(), 100);
                    } else {
                        cPlayer.setInPassiveAvoidance(false);
                    }
                }
            }
        }
    }
     
    My problem here is if the player is attacked multiple times, the BukkitRunnable is triggered multiple times. I need it the runnable will ONLY go through ONCE, after the last time they're hit.

    #isInPassiveAvoidance() is a boolean, that part works fine. I mostly need help with the BukkitRunnable and logic here.

    Here is what's happening if I'm attacked multiple times: [​IMG]
     
  2. Count ticks manually, or if the task is already scheduled, cancel it and schedule a new one.
     
  3. Soo... assign the runnable to an ID and put it in a map with the player's UUID?
     
  4. You can do it any way you want so as to keep the current runnable.
     
  5. drives_a_ford

    Moderator

    BukkitRunnable#runTaskLater returns a BukkitTask. So you'd be best mapping the players' UUIDs to that. And when they get damaged, check if there's already one in there for this player. If there is, BukkitTask#cancel.

    PS: You'll also want to make sure to remove the mapping when the runnable finally runs,
     
  6. Code (Text):
                        if(cPlayer.getPassiveAvoidanceRunnable() != null) {
                            cPlayer.getPassiveAvoidanceRunnable().cancel();

                            cPlayer.setPassiveAvoidanceRunnable((BukkitRunnable) new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceRunnable(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
                        } else {
                            cPlayer.setPassiveAvoidanceRunnable((BukkitRunnable) new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceRunnable(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
                        }
    I am trying this, and it's not actually cancelling the tasks it seems.

    Code (Text):
       
    private transient BukkitRunnable passiveAvoidanceRunnable = null;
    public BukkitRunnable getPassiveAvoidanceRunnable() {
            if(passiveAvoidanceRunnable != null) {
                return passiveAvoidanceRunnable;
            } else {
                return null;
            }
        }

        public void setPassiveAvoidanceRunnable(BukkitRunnable bukkitRunnable) {
            this.passiveAvoidanceRunnable = bukkitRunnable;
        }
    This is in the CPlayer object.
     
  7. drives_a_ford

    Moderator

    Again:
    You're casting the BukkitTask to a BukkitRunnable. You should be getting a ClassCastException.
     
  8. Yup! I was, I noticed it and changed it and it works!

    Final code:

    Listener:
    Code (Text):
        @EventHandler
        public void onDamage(EntityDamageEvent event) {
            Entity ent = event.getEntity();

            if(ent instanceof Player) {
                Player player = (Player) event.getEntity();
                CPlayer cPlayer = ClassesMain.getInstance().cPlayerManager.getCPlayerByUUID(player.getUniqueId());

                if(cPlayer != null) {
                    if(cPlayer.getClassType().equals(ClassType.WIZARD)) {
                        if(cPlayer.getPassiveAvoidanceTask() != null) {
                            cPlayer.getPassiveAvoidanceTask().cancel();

                            cPlayer.setPassiveAvoidanceTask(new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceTask(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
                        } else {
                            cPlayer.setPassiveAvoidanceTask(new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceTask(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
                        }
                    }
                }
            }
        }
    Object:
    Code (Text):
       
    private transient BukkitTask passiveAvoidanceTask = null;
    public BukkitTask getPassiveAvoidanceTask() {
            if(passiveAvoidanceTask != null) {
                return passiveAvoidanceTask;
            } else {
                return null;
            }
        }

        public void setPassiveAvoidanceTask(BukkitTask bukkitTask) {
            this.passiveAvoidanceTask = bukkitTask;
        }
    Thanks again @drives_a_ford
     
  9. drives_a_ford

    Moderator

    Remember the DRY principle.
    You should do the same thing twice.

    In the first piece of code, you've got this twice for no reason:
    Code (Java):

                            cPlayer.setPassiveAvoidanceTask(new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceTask(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
    What you should be doing is (pseudocode):
    Code (Java):

    BukkitTask prevTask = cPlayer.getPrevTask();
    if (prevTask != null) prevTask.cancel();
    cPlayer.setTask(new BukkitRunnable() {
    // stuff
    }.runTaskLater(ClassesMain.getInstance(), 100));
    So you only have one anonymous BukkitRunnable.

    In the second bit of code
    Code (Java):

    public BukkitTask getPassiveAvoidanceTask() {
            if(passiveAvoidanceTask != null) {
                return passiveAvoidanceTask;
            } else {
                return null;
            }
        }
    You can just return the field - if it's null, you're returning null anyway!
     
    • Friendly Friendly x 1
  10. Code (Text):
    BukkitTask passiveAvoidanceTask = cPlayer.getPassiveAvoidanceTask();
                        if(passiveAvoidanceTask != null) {
                            cPlayer.getPassiveAvoidanceTask().cancel();

                            cPlayer.setPassiveAvoidanceTask(new BukkitRunnable() {
                                public void run() {
                                    player.setAbsorptionAmount(4);
                                    player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                    cPlayer.setPassiveAvoidanceTask(null);
                                }
                            }.runTaskLater(ClassesMain.getInstance(), 100));
                        }
    So this

    Edit:
    Code (Text):
                        BukkitTask passiveAvoidanceTask = cPlayer.getPassiveAvoidanceTask();
                        if(passiveAvoidanceTask != null) passiveAvoidanceTask.cancel();

                        cPlayer.setPassiveAvoidanceTask(new BukkitRunnable() {
                            public void run() {
                                player.setAbsorptionAmount(4);
                                player.sendMessage(Messages.getMessage(MessageType.PASSIVE_AVOIDANCE_ACTIVATED));

                                cPlayer.setPassiveAvoidanceTask(null);
                            }
                        }.runTaskLater(ClassesMain.getInstance(), 100));
    Got it. Works PERFECTLY. :D Ty @drives_a_ford got more respect for ford owners now :p
     
    #10 VivianMusic, Mar 26, 2020
    Last edited: Mar 26, 2020