Solved Cancel Runnable, ID via HashMap

Discussion in 'Spigot Plugin Development' started by Ladinn, Apr 27, 2017.

  1. Hey everyone,

    I'm trying to schedule a DelayedTask upon a certain event. After every DelayedTask within the main DelayedTask, the stopScheduler method should check whether or not the player is in a specified WorldGuard region. If they aren't, the main task (ID stored in HashMap) should be canceled.

    However, the taskId isn't getting read from the map, resulting in an error. (The error is for "int taskId = taskMap.get(fed);")

    I've been Googling for the past few hours, but I can't figure it out. What do you think?

    Thanks!
    Code (Text):
        public static HashMap<String, Integer> taskMap = new HashMap<String, Integer>();

        public static void runWarClaim(ProtectedRegion region, String fed, String objective, String objectiveNiceName) {
               
            ArrayList<Player> online = CheckFed.getOnlinePlayersInFed(fed);
       
            for (Player player : online) {
                player.sendMessage(Main.gamePrefix + ChatColor.GREEN + "Securing objective: " + ChatColor.GOLD + objectiveNiceName + ".");
                player.sendMessage(ChatColor.DARK_GRAY + "  - " + ChatColor.GRAY + "60 seconds remaining.");        
            }
               
            taskMap.put(fed, Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                public void run() {
                               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run1(online, objectiveNiceName);                    
                        } }, 200L);
               
                    stopScheduler(region, fed, objectiveNiceName);
               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run2(online, objectiveNiceName);                    
                        } }, 400L);
               
                    stopScheduler(region, fed, objectiveNiceName);
               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run3(online, objectiveNiceName);                    
                        } }, 600L);
               
                    stopScheduler(region, fed, objectiveNiceName);
               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run4(online, objectiveNiceName);                    
                        } }, 800L);
               
                    stopScheduler(region, fed, objectiveNiceName);
               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run5(online, objectiveNiceName);                    
                        } }, 1000L);
               
                    stopScheduler(region, fed, objectiveNiceName);
               
                    Main.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
                        @Override
                        public void run() {
                            run6(objective, objectiveNiceName, fed);                    
                        } }, 1200L);            
                }
            }, 0));
        }

        public static void stopScheduler(ProtectedRegion region, String fed, String objectiveNiceName) {
           
            ArrayList<Player> online = CheckFed.getOnlinePlayersInFed(fed);
       
            @SuppressWarnings("unused")
            boolean isOccupied = false;
       
            for (Player player : online) {
           
                if (player.isDead()) continue;
           
                Location loc = player.getLocation();
                if (Main.getWorldGuard().getRegionManager(player.getWorld()).getApplicableRegions(loc).getRegions().contains(region)) {
                    isOccupied = true;
                    break;
                }
            }
       
            if (isOccupied = false) return;
               
            int taskId = taskMap.get(fed);
            Main.getInstance().getServer().getScheduler().cancelTask(taskId);
            taskMap.remove(fed);
       
            for (Player player : online) {
                player.sendMessage(Main.gamePrefix + ChatColor.RED + "Failed to secure objective:");
                player.sendMessage(ChatColor.DARK_GRAY + "  - " + ChatColor.GRAY + objectiveNiceName);
            }
       
        }
     
    #1 Ladinn, Apr 27, 2017
    Last edited: Apr 27, 2017
  2. I think you should just use a BukkitRunnable and use it's built in cancel() method rather than fooling around with task IDs.
     
  3. How could I do that from the stopScheduler method if its not in the Runnable?
     
  4. maldahleh

    Wiki Team

    Your code is hard to follow, don't use static all over and instead of Main.getInstance() pass an instance of main to a constructor and use that, also use lambdas, much easier to follow.
     
    • Informative Informative x 1
  5. You said you want a task to run when an event happens. Ok. You want this task to repeat until the player leaves a certain wg region. Ok. This is simpler than you're making it.

    1) create and run a BukkitRunnable on a repeating timer when an event occurs.
    2) in this BukkitRunnable do your check for player in the wg region.
    3) if the player is not inside the region simply call cancel(); then return;
    4) if the BukkitRunnable made it this far then the player is in the region and you can proceed with whatever you're doing here.

    There is no need for a master task to handle creating and cancelling tasks here. Simply let each task handle itself and all will be fine ^_^
     
  6. So somewhat of a "check if player is in region" repeating task? How would it cancel the main delayed task (thus canceling the tasks inside)? The methods in the main delayed task- i.e, run1, run2, run3, etc- need to run in that specific order every ten seconds. Would a repeating task just restart the cycle every time the timer is up?

    Thanks for the advice, honestly- I'm still working on my code structuring across the board so that's certainly helpful information.
     
  7. So if I change the main scheduler to use new BukkitRunnable() { as opposed to what I was using previously, new Runnable() {, I'm able to cancel the task correctly, but I'm getting depreciation warnings. Should I not use this or can I just ignore it?
     
  8. How are you using it? It's deprecated to use the scheduler. Instead, BukkitRunnables start themselves. They were designed to be easier to use (no worrying about task ids, etc) than regular runnables with the scheduler, but under the hood it's the same thing.

    Pseudo code to help explain.
    Code (Text):
    Runnable runnable = new Runnable();
    int taskId = getScheduler().runTask(runnable, plugin);
    getScheduler().cancelTask(taskId);
    Vs.
    Code (Text):
    BukkitRunnable runnable = new BukkitRunnable();
    runnable.runTask(plugin);
    runnable.cancel();
    Inside the BukkitRunnable you can this.cancel() because it's self maintained. With a runnable you can't, you would need it's id which it's not aware of and you'd have to keep track of. Etc.

    I would give proper examples in context, but I'm on phone ^_^

    https://hub.spigotmc.org/javadocs/spigot/org/bukkit/scheduler/BukkitRunnable.html

    More examples: http://bukkit.gamepedia.com/Scheduler_Programming
     
    #8 BillyGalbreath, Apr 27, 2017
    Last edited: Apr 27, 2017
    • Informative Informative x 2
    • Agree Agree x 1
  9. WAS

    WAS

    I think you mean create a Main.getInstance() method or pass an instance of the main to a constructor, right?

    You could easily create a class to handle multiple types of tasks, and create new instances with your properties, start tasks on that class (which extends BukkitRunnable) with your unique timings.
     
  10. Thanks for this! I think I've almost figured it out, I'm using this code now:
    Code (Text):
    taskMap.put(fed, new BukkitRunnable() {
    ...
    }.runTask(instance));
    Then checking within the runnable whether or not the player is in the region. If not,
    Code (Text):
    BukkitTask task = taskMap.get(fed);
    task.cancel();
    taskMap.remove(fed);
    However, the error I'm getting now is:
    Code (Text):
    [02:03:14] [Server thread/WARN]: [VoxelaGov] Task #269110952 for VoxelaGov v1.0 generated an exception
    java.lang.NullPointerException
            at com.voxela.gov.events.war.WarRunnable.stopScheduler(WarRunnable.java:107) ~[?:?]
            at com.voxela.gov.events.war.WarRunnable$1.run(WarRunnable.java:51) ~[?:?]
            at org.bukkit.craftbukkit.v1_11_R1.scheduler.CraftTask.run(CraftTask.java:71) ~[spigot.jar:git-Spigot-b605b31-91c3152]
            at org.bukkit.craftbukkit.v1_11_R1.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:353) [spigot.jar:git-Spigot-b605b31-91c3152]
            at net.minecraft.server.v1_11_R1.MinecraftServer.D(MinecraftServer.java:738) [spigot.jar:git-Spigot-b605b31-91c3152]
            at net.minecraft.server.v1_11_R1.DedicatedServer.D(DedicatedServer.java:399) [spigot.jar:git-Spigot-b605b31-91c3152]
            at net.minecraft.server.v1_11_R1.MinecraftServer.C(MinecraftServer.java:678) [spigot.jar:git-Spigot-b605b31-91c3152]
            at net.minecraft.server.v1_11_R1.MinecraftServer.run(MinecraftServer.java:576) [spigot.jar:git-Spigot-b605b31-91c3152]
            at java.lang.Thread.run(Thread.java:745) [?:1.8.0_91]
     
    If the BukkitRunnable instance is being put in the hashmap, how is it null after it began running?
     
  11. runTask() returns void. I'm surprised your ideas didn't underline that as an error when trying to store it in the map.

    1) create the task.
    2) store the task.
    3) finally run the task.

    But, what is the purpose of even storing the task in a map at this point? You don't need to retrieve the task from the map from within the task just to cancel it. It's self aware. Just call this.cancel();

    All this
    Code (Text):
    BukkitTask task = taskMap.get(fed);'
    task.cancel();
    taskMap.remove(fed);
    Should be simply
    Code (Text):
    this.cancel();
    then you don't need the map at all.
     
  12. I would, but it would be canceled from a separate method. Within the runnable is a call to the stopScheduler method- in there is where the checks are performed, so I can't cancel it without getting the runnable. I could just put the entire method within the runnable, but I'd have to copy it multiple times and I may have to change it sometime in the future (see the original code).

    So, is there another way to cancel a BukkitRunnable from a separate method?
     
  13. Then just give 'this' to the stopScheduler method.

    Code (Text):
    stopScheduler(this);
    That method then has the BukkitRunnable instance and can do what it wants.
     
  14. I'm getting a syntax error when I use "this" as one of the few arguments for the method, same if I use it alone. Am I not passing it correctly?

    Here's what I'm dealing with again:
    Code (Text):
        public static void stopScheduler(this, ProtectedRegion region, String fed, String objectiveNiceName) {
               
            ArrayList<Player> online = CheckFed.getOnlinePlayersInFed(fed);
           
            @SuppressWarnings("unused")
            boolean isOccupied = false;
           
            for (Player player : online) {
               
                if (player.isDead()) continue;
               
                Location loc = player.getLocation();
                if (Main.getWorldGuard().getRegionManager(player.getWorld()).getApplicableRegions(loc).getRegions().contains(region)) {
                    isOccupied = true;
                    break;
                }
            }
           
            if (isOccupied = false) return;
           
            this.cancel();
           
            for (Player player : online) {
                player.sendMessage(Main.gamePrefix + ChatColor.RED + "Failed to secure objective:");
                player.sendMessage(ChatColor.DARK_GRAY + "  - " + ChatColor.GRAY + objectiveNiceName);
            }
           
        }
     
  15. What?
    Code (Text):
    public static void stopScheduler(this, ProtectedRegion region, String fed, String objectiveNiceName) {
    Here.
    Code (Text):
    public static void stopScheduler(BukkitRunnable runnable, ProtectedRegion region, String fed, String objectiveNiceName) {
    Seems basic things are escaping you now. Calm down :p I know it's new, and stressful. Just breathe. The worst thing any coder can do is over complicate or over think something simple.
     
    • Winner Winner x 1
  16. ...that's what happens when I lose consciousness this late at night- not even sure how I got there lol. A million thanks for your help, you just ended my multiple-hour long debugging!

    You're the man!