1

I am trying to make a repeating runnable only last a certain amount of times, but I can't find a method to cancel the repeating runnable once an integer reaches a certain number.

                Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
                public void run() {

                    Random rand = new Random();
                    int rnum = rand.nextInt(main.allowed.size()) + 1;

                    e.getPlayer().getInventory().addItem(main.allowed.get(rnum));

                    for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
                        // Something here.
                    }

                }
             }, 0L, 0L);

Edit:
I just needed to know how to stop the runnable from inside that for statement. I got that idea from that link (How to stop a Runnable scheduled for repeated execution after a certain number of executions)

  • 2
    `if (yourInt >= yourValue) return;` ? – fantaghirocco Aug 29 '18 at 15:40
  • 1
    Possible duplicate of [How to stop a Runnable scheduled for repeated execution after a certain number of executions](https://stackoverflow.com/questions/7269294/how-to-stop-a-runnable-scheduled-for-repeated-execution-after-a-certain-number-o) – CodeMonkey Aug 29 '18 at 15:41

1 Answers1

0

Please tell me if I am wrong but I think that you don't want to cancel the runnable inside the for loop. That will stop the execution at that time, but I assume that it won't prevent it to be executed again and again because it is scheduled indefinitely. So my approach would be to unschedule it rather than terminate it inside the loop.

With this approach, I think you can do something like this, even tho it is a bit tricky:

//We use atomicInteger because the Runnable will be in other thread
AtomicInteger currentIteration = new AtomicInteger(0);

int maxAttempts = 100;

Map<String, Integer> idToProcessIdMap = new HashMap<>();
final String customProcessId = UUID.randomUUID().toString();

Consumer<String> endProcessConsumer = ((generatedId) -> {
  int processId = idToProcessIdMap.get(generatedId);
  Bukkit.getScheduler().cancelTask(processId);
});

int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
  public void run() {

    Random rand = new Random();
    int rnum = rand.nextInt(main.allowed.size()) + 1;

    e.getPlayer().getInventory().addItem(main.allowed.get(rnum));

    for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
      // Something here.
    }

    int currentIt = currentIteration.incrementAndGet();
    if(currentIt > maxAttempts){
      endProcessConsumer.accept(customProcessId);
    }
  }
}, 0L, 0L);

idToProcessIdMap.put(customProcessId, taskId);

Edit: Simplified version

AtomicInteger currentIteration = new AtomicInteger(0);
int maxAttempts = 100;

AtomicInteger processId = new AtomicInteger();

int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
  public void run() {

    Random rand = new Random();
    int rnum = rand.nextInt(main.allowed.size()) + 1;

    e.getPlayer().getInventory().addItem(main.allowed.get(rnum));

    for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
      // Something here.
    }

    int currentIt = currentIteration.incrementAndGet();
    if(currentIt > maxAttempts){
      Bukkit.getScheduler().cancelTask(processId.get());
    }
  }
}, 0L, 0L);

processId.set(taskId);

What I do in the code is first to create a variable to identify in which iteration we are. Then I create a custom identifier for the process you are running and link it with the real process Id in a HashMap. We need to do this because when we run the process, we still don't know which is its id and therefore we won't be able to stop it directly

Also, I create a consumer which I can call inside the process when we reach the max execution times in order to unschedule itself.

Juan Bermudez
  • 430
  • 3
  • 6
  • it works amazingly, I am just wondering why you need to use the int TaskID at the beginning of the runnable and add the processID to the map? – Bryan Correll Aug 29 '18 at 16:31
  • Hi Bryan. When we first run the runnable, it does not know which will be its task because this is given by Bukkit. The point is that we need to access to the taskId inside the runnable, but taskId is given after the runnable is run, so we need to encapsulate it in another final object in order to get its value. I refactored a bit the code so it may be easier to understand the concept (I didn't test it but it should work fine) – Juan Bermudez Aug 30 '18 at 08:56