How to cancel this RepeatingTask

Discussion in 'Plugin Development' started by CreeperShift, Apr 29, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    CreeperShift

    Hey, I'm finding myself in a little trouble right now:

    On the ProjectileLaunchEvent,

    after an arrow is shot I'm trying to track it's location via a repeating task every X ticks, and cancel itself once it hit's water and turns it into ice.
    Code:
                                final Entity E = event.getEntity();
                                plugin
                                .getServer()
                                .getScheduler()
                                .runTaskTimer(plugin,
                                        new Runnable() {
                                            @Override
                                            public void run() {
     
                                                Location loc = E.getLocation();
                                                if (loc.getBlock().getType() == Material.WATER) {
                                                    loc.getBlock().setType(Material.ICE);
                                                }
                                            }
                                        }, 3, 3);
    Now I'm stuck and can't figure out how to make it cancel itself once it's done or isn't changing it's location anymore. I've done some searching and found solutions, but they wont work for me because I need a new repeating task every time a player shoots the arrow. :/
     
  2. Offline

    blablubbabc

    Currently, the way you do it, you could store the BukkitTask object or it's id, which is returned by plugin.getServer().getScheduler().runTaskTimer(..); together with the projectiles id, so maybe in a hashmap<Integer (id), Integer (taskid)> tasks .. and later retrieve the tasks id from there to cancle it via plugin.getServer().getScheduler().cancleTask(id);

    Also you could make your own class that implements Runnable and store the task id there on creation.

    Or, how I would do it, don't use a repeating task: instead you could only store the projectiles ids and in the ProjectileHitEvent you check if the projectile was one of yours and do your stuff (which could cause to leaks, if the projectile somehow else gets removed, so you would have to periodically check through your list of projecitles id if they are still valid/ if the prijectiles still exist), OR you attach meta data to the projectile and check for the metadata on projectile hit and if it exists, you do your stuff, like .. Ignore this. I didn't saw thought about that projhectilehitevent might nott get called on "hitting water".

    However, you should also think about what happens to the entries in the hashmap, if your projectile doesn't hit water in a certain time, or gets removed randomly somehow.

    Also instead of 1 task for each projectile you could run one task all the time for all your projectiles. In the prjectile launch you add your prjectile to a list or whatever, and the task iterates through all projectiles in this list and removes them if it is inside water or after a certain time.

    Edit: You could also only start this one task when a projectile is added and the task was not running before, and cancles the task again, if there are no more projectiles in your list anymore.
     
  3. Offline

    CreeperShift

    Hmm, I see. However wouldn't creating more repeatingtasks be a bit overkill? I've been kind of refusing to add this arrow for the past month because I'm worried it might lag stuff, so you see my problem here :p.

    However, with a bit of tinkering I found out this works:

    Code:
                                final Entity E = event.getEntity();
     
                                new BukkitRunnable() {
                                    @Override
                                    public void run() {
                                        Location loc = E.getLocation();
     
                                        if (loc.getBlock().getType() == Material.WATER) {
                                            loc.getBlock().setType(Material.ICE);
                                            launcher.sendMessage("ice!");
                                            this.cancel();
                                        } else if (loc.getBlock().getType() == Material.STATIONARY_WATER) {
                                            loc.getBlock().setType(Material.ICE);
                                            launcher.sendMessage("ice!");
                                            this.cancel();
     
                                        } else if (loc.getBlock().getType() != Material.AIR) {
     
                                            this.cancel();
     
                                        }
                                    }
     
                                }.runTaskTimer(plugin, 1, 3);
    If this is bad practice or anything I should avoid, please tell me :3
     
  4. Offline

    blablubbabc

    you should also check if the projectile is still not dead / still valid, and cancle the task in this case too.

    "However wouldn't creating more repeatingtasks be a bit overkill?"
    I don't know. I guess it shouldn't be that much. You could make some guesses about how many players launch projectiles you monitor per time a projectile needs to land in average and if you think this is much.
    You are currently creating one new BukkitRunnable for each Projectile, thats why I suggested that you only create one task and a list and the task checks this list all the time for the projectiles you want to monitor. So simple add/remove projectiles to/from this list to monitor through this one task.
     
  5. Offline

    CreeperShift

    blablubbabc

    Hmm I guess I wanted to avoid having to iterate through a possibly large-ish list.

    Anyways

    I wanted to do that but I'm not sure how without using lists. Delayed task and just remove it after X seconds regardless?
     
  6. Offline

    Sagacious_Zed Bukkit Docs

    If you follow the wiki article about scheduler programming. A BukkitRunnable can cancel it's own execution. It is by far easier to extend BukkitRunnable with the logic you would like to repeat, and invoke the proper method ON your BukkitRunnable.
     
  7. Offline

    CreeperShift

    What would be different than doing what I posted above? Does it have any significant advantages?
     
  8. Offline

    Sagacious_Zed Bukkit Docs

    The short answer is everything (to some extent).
    It probably will become easier to reason and work with.
     
Thread Status:
Not open for further replies.

Share This Page