From e5ebed076db8cd7e533c966e79eab586b8aa8950 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 16:32:55 +0100 Subject: [PATCH 01/19] add files --- config/sidebar.paper.ts | 1 + docs/paper/dev/api/scheduler.md | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 docs/paper/dev/api/scheduler.md diff --git a/config/sidebar.paper.ts b/config/sidebar.paper.ts index 59f3eb33e..58236b48b 100644 --- a/config/sidebar.paper.ts +++ b/config/sidebar.paper.ts @@ -100,6 +100,7 @@ const paper: SidebarsConfig = { }, "dev/api/pdc", "dev/api/custom-inventory-holder", + "dev/api/scheduler", ], }, ], diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md new file mode 100644 index 000000000..fde9886ee --- /dev/null +++ b/docs/paper/dev/api/scheduler.md @@ -0,0 +1,3 @@ +--- +slug: /dev/scheduler +--- From c6edc76cb9890b686078cade95ff417278a4c445 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 16:41:47 +0100 Subject: [PATCH 02/19] add tick explanation, converting ticks and obtaining the scheduler --- docs/paper/dev/api/scheduler.md | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index fde9886ee..296bf093d 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -1,3 +1,54 @@ --- slug: /dev/scheduler --- + +# Scheduler + +The `BukkitScheduler` can be used to schedule your code to be run later or run it repeatedly. + +## What is a tick? + +Every game runs something called a game loop which essentially executes all the logic of the game over and over, +a single execution of that loop is called a 'tick'. + +In Minecraft's case the amount of ticks per second is 20, meaning that the game loop is executed 20 times per second. +With some math we can see that 1 tick is equal to 50 milliseconds. A tick taking more than 50ms to execute is the moment +when your server starts to fall behind on its work and lag. + +### Converting between SI units and Minecraft ticks + +Every method of the scheduler that takes a delay or period uses ticks as a unit of time. + +:::warning + +The above also applies to the asynchronous methods. If you need a scheduler with millisecond precision, create your own +[`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html). + +::: + +Converting from human units to ticks and back is as simple as: +`ticks = seconds * 20` +`seconds = ticks / 20` + +:::note + +In Java and Kotlin to not lose precision you must use `ticks / 20.0`. + +::: + +You can make your code more readable by using the +[`TimeUnit`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/TimeUnit.html) +enum, e.g. to convert 5 minutes to ticks and back: +`TimeUnit.MINUTES.toSeconds(5) * 20` +`TimeUnit.SECONDS.toMinutes(ticks / 20)` + +## Obtaining the scheduler + +To obtain a scheduler you can use the instance method on the `Server` class, e.g. in your `onEnable` method: + +```java +@Override +public void onEnable() { + BukkitScheduler scheduler = this.getServer().getScheduler(); +} +``` \ No newline at end of file From 4788e9f9a80dea60bd6c942a710e0f1581512a3d Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 16:49:17 +0100 Subject: [PATCH 03/19] note the async part --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 296bf093d..c4152f188 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -21,7 +21,7 @@ Every method of the scheduler that takes a delay or period uses ticks as a unit :::warning -The above also applies to the asynchronous methods. If you need a scheduler with millisecond precision, create your own +The above also applies to the asynchronous methods. If you need **an asynchronous scheduler** with millisecond precision, create your own [`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html). ::: From 8912523c3d97637ef8975622029b1b0b10dc17ab Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 17:24:51 +0100 Subject: [PATCH 04/19] arguments to schedule a task, difference between and usage of `Runnable` and `Consumer` --- docs/paper/dev/api/scheduler.md | 85 +++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index c4152f188..89bb9b175 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -51,4 +51,89 @@ To obtain a scheduler you can use the instance method on the `Server` class, e.g public void onEnable() { BukkitScheduler scheduler = this.getServer().getScheduler(); } +``` + +## Scheduling tasks + +Scheduling a task requires you to pass: +- your plugin's instance, +- the code to run, either with a `Runnable` or `Consumer` (the differences and usage are explained below), +- the delay in ticks before the task should run, +- the period in ticks between each execution of the task if you're scheduling a repeating task. + +### Using `Runnable` + +The `Runnable` interface is used for the simplest tasks that don't require a `BukkitTask` instance. + +You can either implement it in a separate class, e.g.: + +```java +public class MyRunnableTask implements Runnable { + + private final MyPlugin plugin; + + public MyRunnableTask(MyPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + this.plugin.getServer().broadcast(Component.text("Hello, World!")); + } + +} +``` +```java +scheulder.runTaskLater(plugin, new MyRunnableTask(plugin), 20); +``` + +Or use a lambda expression which is great for simple and short tasks: + +```java +scheduler.runTaskLater( + plugin, + // Lambda: + () -> { + this.plugin.getServer().broadcast(Component.text("Hello, World!") + }, + // End of the lambda + 20); +``` + +### Using `Consumer` + +The Consumer interface is used for tasks that require a `BukkitTask` instance (usually in repeated tasks), +e.g. when you want to cancel the task from inside it. + +You can either implement it in a separate class similarly to the `Runnable`, e.g.: + +```java +public class MyConsumerTask implements Consumer { + + private final LivingEntity entity; + + public MyConsumerTask(LivingEntity entity) { + this.entity = entity; + } + + @Override + public void accept(BukkitTask task) { + if(this.entity.isDead()) task.cancel(); // The entity died, there's no point + // in running the code anymore. + this.entity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1)); + } + +} +``` +```java +scheduler.runTaskTimer(plugin, new MyConsumerTask(someEntity), 0, 20); +``` + +Or use a lambda expression which again is much cleaner for short and simple tasks: + +```java +scheduler.runTaskTimer(plugin, /* Lambda: */ task -> { + if(this.entity.isDead()) task.cancel(); + this.entity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1)); +} /* End of the lambda */, 0, 20); ``` \ No newline at end of file From a3e325aa7d6c22c8a3bf36d784790164a3abd1a7 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 17:45:31 +0100 Subject: [PATCH 05/19] replace `SI units` with `human units` --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 89bb9b175..af7dbc027 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -15,7 +15,7 @@ In Minecraft's case the amount of ticks per second is 20, meaning that the game With some math we can see that 1 tick is equal to 50 milliseconds. A tick taking more than 50ms to execute is the moment when your server starts to fall behind on its work and lag. -### Converting between SI units and Minecraft ticks +### Converting between human units and Minecraft ticks Every method of the scheduler that takes a delay or period uses ticks as a unit of time. From cd47320b5a7a6e93de34b6abf2ce69c1ad1698e9 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Fri, 17 Mar 2023 17:52:11 +0100 Subject: [PATCH 06/19] not Java tutorial --- docs/paper/dev/api/scheduler.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index af7dbc027..ea08bac41 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -30,12 +30,6 @@ Converting from human units to ticks and back is as simple as: `ticks = seconds * 20` `seconds = ticks / 20` -:::note - -In Java and Kotlin to not lose precision you must use `ticks / 20.0`. - -::: - You can make your code more readable by using the [`TimeUnit`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/TimeUnit.html) enum, e.g. to convert 5 minutes to ticks and back: From 2402cdf505b0024516d6881573155e53ea30b05b Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:22:48 +0100 Subject: [PATCH 07/19] basic sync examples --- docs/paper/dev/api/scheduler.md | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index ea08bac41..eda8c427e 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -130,4 +130,74 @@ scheduler.runTaskTimer(plugin, /* Lambda: */ task -> { if(this.entity.isDead()) task.cancel(); this.entity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1)); } /* End of the lambda */, 0, 20); +``` + +## Tasks on the main thread + +### A task to run later once + +This task will run a single time after the delay specified in ticks, in this case 1 second. + +```java +scheduler.runTaskLater(plugin, () -> { + server.broadcast(Component.text("Hello, World!")); +}, 20); +``` + +### A task to run later repeatedly + +This task will run repeatedly, first time after the delay specified in ticks - in this case 1 second - +and then every period specified in ticks - in this case 5 seconds. + +```java +scheduler.runTaskTimer(plugin, () -> { + server.broadcast(Component.text("Hello, World!")); +}, 20, 5 * 20); +``` + +### A repeating task to be canceled later + +This task will run repeatedly, first time after 10 seconds, and then every 5 seconds. After 10 minutes +the task will be canceled entirely. + +```java +BukkitTask task = scheduler.runTaskTimer(plugin, () -> { + server.broadcast(Component.text("Hello, World!")); +}, 10 * 20, 5 * 20); + +scheduler.runTaskLater(plugin, () -> task.cancel(), TimeUnit.MINUTES.toSeconds(10) * 20); +``` + +### A repeating task with a dynamic period + +This task will run repeatedly, first time after 10 seconds, and then every 20-100 ticks (1-5 seconds) chosen randomly. + +```java +public class MyDynamicTask implements Runnable { + + private final Random random = new Random(); + private final MyPlugin plugin; + + public MyDynamicTask(MyPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + this.plugin.getServer().broadcast(Component.text("Hello, World!")); + this.plugin.getServer().getScheduler().runTaskLater( + this.plugin, + this, + this.random.nextInt(20, 101) + ); + } + +} +``` + +Note that we schedule the task again in the `run` method, when scheduling initially we will also +use the `runTaskLater` method and not `runTaskTimer`. + +```java +scheduler.runTaskLater(plugin, new MyDynamicTask(plugin), 10 * 20); ``` \ No newline at end of file From 3f4a10c132da2469dcf52b9a2c7925e88957bdb8 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:23:31 +0100 Subject: [PATCH 08/19] fix typo --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index eda8c427e..344e713a1 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -78,7 +78,7 @@ public class MyRunnableTask implements Runnable { } ``` ```java -scheulder.runTaskLater(plugin, new MyRunnableTask(plugin), 20); +scheduler.runTaskLater(plugin, new MyRunnableTask(plugin), 20); ``` Or use a lambda expression which is great for simple and short tasks: From a512fd80af0c08c984da43d645e744e99e19aa48 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:34:26 +0100 Subject: [PATCH 09/19] ensure task reschedule (try..finally) --- docs/paper/dev/api/scheduler.md | 34 +++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 344e713a1..a84221bb1 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -184,20 +184,34 @@ public class MyDynamicTask implements Runnable { @Override public void run() { - this.plugin.getServer().broadcast(Component.text("Hello, World!")); - this.plugin.getServer().getScheduler().runTaskLater( - this.plugin, - this, - this.random.nextInt(20, 101) - ); + try { + this.plugin.getServer().broadcast(Component.text("Hello, World!")); + } finally { + this.plugin.getServer().getScheduler().runTaskLater( + this.plugin, + this, + this.random.nextInt(20, 101) + ); + } } } ``` -Note that we schedule the task again in the `run` method, when scheduling initially we will also -use the `runTaskLater` method and not `runTaskTimer`. - ```java scheduler.runTaskLater(plugin, new MyDynamicTask(plugin), 10 * 20); -``` \ No newline at end of file +``` + +:::note + +We schedule the task again in the `run` method, that's why to schedule the task initially +we use the `runTaskLater` method too and not `runTaskTimer`. + +::: + +:::info + +We use a try-finally block to ensure the task is scheduled again. Otherwise if the methods before scheduling +the tasks would throw an exception the task would never be scheduled again. + +::: \ No newline at end of file From 1dfbc9de6eebb7055d3360394c7b7813ab4a6c83 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:35:40 +0100 Subject: [PATCH 10/19] rename --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index a84221bb1..cbcf47fe1 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -2,7 +2,7 @@ slug: /dev/scheduler --- -# Scheduler +# Task Scheduler The `BukkitScheduler` can be used to schedule your code to be run later or run it repeatedly. From 181ace1d0beef9885b6a887194f7d8342f0a4615 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:39:22 +0100 Subject: [PATCH 11/19] fix small inconsistency --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index cbcf47fe1..e29ce7d5e 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -96,7 +96,7 @@ scheduler.runTaskLater( ### Using `Consumer` -The Consumer interface is used for tasks that require a `BukkitTask` instance (usually in repeated tasks), +The `Consumer` interface is used for tasks that require a `BukkitTask` instance (usually in repeated tasks), e.g. when you want to cancel the task from inside it. You can either implement it in a separate class similarly to the `Runnable`, e.g.: From a7b3f42c0efc804855339947d5cd21f089b9511f Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 19 Mar 2023 10:41:10 +0100 Subject: [PATCH 12/19] fix typo --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index e29ce7d5e..1ff6c07a4 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -212,6 +212,6 @@ we use the `runTaskLater` method too and not `runTaskTimer`. :::info We use a try-finally block to ensure the task is scheduled again. Otherwise if the methods before scheduling -the tasks would throw an exception the task would never be scheduled again. +the task would throw an exception the task would never be scheduled again. ::: \ No newline at end of file From 170d39322421447a74d8f57aa1779c9ccf4c08cd Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 26 Mar 2023 01:10:59 +0100 Subject: [PATCH 13/19] yeet 1st warning, sync and async difference --- docs/paper/dev/api/scheduler.md | 51 ++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 1ff6c07a4..0e019ff72 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -19,13 +19,6 @@ when your server starts to fall behind on its work and lag. Every method of the scheduler that takes a delay or period uses ticks as a unit of time. -:::warning - -The above also applies to the asynchronous methods. If you need **an asynchronous scheduler** with millisecond precision, create your own -[`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html). - -::: - Converting from human units to ticks and back is as simple as: `ticks = seconds * 20` `seconds = ticks / 20` @@ -51,11 +44,47 @@ public void onEnable() { Scheduling a task requires you to pass: - your plugin's instance, -- the code to run, either with a `Runnable` or `Consumer` (the differences and usage are explained below), +- the code to run, either with a `Runnable` or `Consumer`, - the delay in ticks before the task should run, - the period in ticks between each execution of the task if you're scheduling a repeating task. -### Using `Runnable` +### Difference between synchronous and asynchronous tasks + +#### Synchronous tasks (on the main thread) + +Synchronous tasks are tasks that are executed on the main server thread. This is the same +thread that handles all game logic. + +All tasks scheduled on the main thread will affect the server's performance. If your task +is making web requests, accessing files, databases or otherwise time-consuming operations you should consider using +an asynchronous task. + +#### Asynchronous tasks (off the main thread) + +Asynchronous tasks are tasks that are executed on separate threads, therefore will not affect +your server's performance. + +:::warning + +**You cannot safely access the Bukkit API from within asynchronous tasks**. If a method is not explicitly marked +as thread-safe or isn't obviously designed to be used asynchronously **you will eventually encounter errors**. + +::: + +:::info + +While the tasks are executed on separate threads, they are still started from the main thread +and will be affected if the server is lagging, an example would be 20 ticks not being exactly 1 second. + +If you need a scheduler that runs independently of the server consider using your own +[`ScheduledExecutorService`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html). +You can follow [this guide](https://www.baeldung.com/java-executor-service-tutorial#ScheduledExecutorService) to learn how to use it. + +::: + +### Difference between `Runnable` and `Consumer` + +#### Using `Runnable` The `Runnable` interface is used for the simplest tasks that don't require a `BukkitTask` instance. @@ -94,7 +123,7 @@ scheduler.runTaskLater( 20); ``` -### Using `Consumer` +#### Using `Consumer` The `Consumer` interface is used for tasks that require a `BukkitTask` instance (usually in repeated tasks), e.g. when you want to cancel the task from inside it. @@ -132,7 +161,7 @@ scheduler.runTaskTimer(plugin, /* Lambda: */ task -> { } /* End of the lambda */, 0, 20); ``` -## Tasks on the main thread +## Examples of tasks on the main thread ### A task to run later once From 5e70ed34062e14790e6a30319429dc71ccc75718 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 26 Mar 2023 01:19:16 +0100 Subject: [PATCH 14/19] fix ooga booga language --- docs/paper/dev/api/scheduler.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 0e019ff72..131954df1 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -240,7 +240,7 @@ we use the `runTaskLater` method too and not `runTaskTimer`. :::info -We use a try-finally block to ensure the task is scheduled again. Otherwise if the methods before scheduling -the task would throw an exception the task would never be scheduled again. +We use a try-finally block to ensure the task is scheduled again, without it if an exception is +thrown before scheduling the task, it will never run again. ::: \ No newline at end of file From cd14a37ec82b6dc3f24bef1bf066f0ca1cd2867c Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Sun, 26 Mar 2023 01:24:20 +0100 Subject: [PATCH 15/19] flip the sentence --- docs/paper/dev/api/scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 131954df1..699b04074 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -46,7 +46,7 @@ Scheduling a task requires you to pass: - your plugin's instance, - the code to run, either with a `Runnable` or `Consumer`, - the delay in ticks before the task should run, -- the period in ticks between each execution of the task if you're scheduling a repeating task. +- if you're scheduling a repeating task - the period in ticks between each execution of the task. ### Difference between synchronous and asynchronous tasks From daa5513aba8bd42fca310b36434846f1de849da2 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Thu, 13 Jul 2023 19:23:48 +0200 Subject: [PATCH 16/19] reword what a tick is --- docs/paper/dev/api/scheduler.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 699b04074..49a6dbcd4 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -11,8 +11,8 @@ The `BukkitScheduler` can be used to schedule your code to be run later or run i Every game runs something called a game loop which essentially executes all the logic of the game over and over, a single execution of that loop is called a 'tick'. -In Minecraft's case the amount of ticks per second is 20, meaning that the game loop is executed 20 times per second. -With some math we can see that 1 tick is equal to 50 milliseconds. A tick taking more than 50ms to execute is the moment +In Minecraft's case the amount of ticks per second is 20, or one tick every 50 milliseconds, +meaning that the game loop is executed 20 times per second. A tick taking more than 50ms to execute is the moment when your server starts to fall behind on its work and lag. ### Converting between human units and Minecraft ticks From 6d2af285cc1521664b8accc25f80ebcd6a38086b Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Thu, 13 Jul 2023 19:29:37 +0200 Subject: [PATCH 17/19] fix code errors --- docs/paper/dev/api/scheduler.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 49a6dbcd4..36d28615f 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -117,7 +117,7 @@ scheduler.runTaskLater( plugin, // Lambda: () -> { - this.plugin.getServer().broadcast(Component.text("Hello, World!") + this.plugin.getServer().broadcast(Component.text("Hello, World!")); }, // End of the lambda 20); @@ -141,8 +141,11 @@ public class MyConsumerTask implements Consumer { @Override public void accept(BukkitTask task) { - if(this.entity.isDead()) task.cancel(); // The entity died, there's no point - // in running the code anymore. + if(this.entity.isDead()) { + task.cancel(); // The entity died, there's no point + return; // in running the code anymore. + } + this.entity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1)); } @@ -156,7 +159,10 @@ Or use a lambda expression which again is much cleaner for short and simple task ```java scheduler.runTaskTimer(plugin, /* Lambda: */ task -> { - if(this.entity.isDead()) task.cancel(); + if(this.entity.isDead()) { + task.cancel(); + return; + } this.entity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1)); } /* End of the lambda */, 0, 20); ``` From 8ab3f4dddad40ba6a4fb1feca35ec8eb8d1773cb Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Thu, 13 Jul 2023 19:32:49 +0200 Subject: [PATCH 18/19] remove dynamic period --- docs/paper/dev/api/scheduler.md | 50 +-------------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 36d28615f..4f4eacb24 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -201,52 +201,4 @@ BukkitTask task = scheduler.runTaskTimer(plugin, () -> { }, 10 * 20, 5 * 20); scheduler.runTaskLater(plugin, () -> task.cancel(), TimeUnit.MINUTES.toSeconds(10) * 20); -``` - -### A repeating task with a dynamic period - -This task will run repeatedly, first time after 10 seconds, and then every 20-100 ticks (1-5 seconds) chosen randomly. - -```java -public class MyDynamicTask implements Runnable { - - private final Random random = new Random(); - private final MyPlugin plugin; - - public MyDynamicTask(MyPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void run() { - try { - this.plugin.getServer().broadcast(Component.text("Hello, World!")); - } finally { - this.plugin.getServer().getScheduler().runTaskLater( - this.plugin, - this, - this.random.nextInt(20, 101) - ); - } - } - -} -``` - -```java -scheduler.runTaskLater(plugin, new MyDynamicTask(plugin), 10 * 20); -``` - -:::note - -We schedule the task again in the `run` method, that's why to schedule the task initially -we use the `runTaskLater` method too and not `runTaskTimer`. - -::: - -:::info - -We use a try-finally block to ensure the task is scheduled again, without it if an exception is -thrown before scheduling the task, it will never run again. - -::: \ No newline at end of file +``` \ No newline at end of file From 1e4872dab45175fb372f7b315ab71d49e59edc39 Mon Sep 17 00:00:00 2001 From: Oliwier Miodun Date: Thu, 13 Jul 2023 19:47:45 +0200 Subject: [PATCH 19/19] rewrite cancelling a repeating task --- docs/paper/dev/api/scheduler.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/docs/paper/dev/api/scheduler.md b/docs/paper/dev/api/scheduler.md index 4f4eacb24..b4648bbc7 100644 --- a/docs/paper/dev/api/scheduler.md +++ b/docs/paper/dev/api/scheduler.md @@ -123,7 +123,7 @@ scheduler.runTaskLater( 20); ``` -#### Using `Consumer` +#### Using `Consumer` {#using-consumerbukkittask} The `Consumer` interface is used for tasks that require a `BukkitTask` instance (usually in repeated tasks), e.g. when you want to cancel the task from inside it. @@ -192,13 +192,6 @@ scheduler.runTaskTimer(plugin, () -> { ### A repeating task to be canceled later -This task will run repeatedly, first time after 10 seconds, and then every 5 seconds. After 10 minutes -the task will be canceled entirely. - -```java -BukkitTask task = scheduler.runTaskTimer(plugin, () -> { - server.broadcast(Component.text("Hello, World!")); -}, 10 * 20, 5 * 20); - -scheduler.runTaskLater(plugin, () -> task.cancel(), TimeUnit.MINUTES.toSeconds(10) * 20); -``` \ No newline at end of file +Cancelling a repeating task requires you to have an instance of a `BukkitTask`. +After obtaining it, simply use the `cancel()` method. +The example on how to use a [`Consumer`](#using-consumerbukkittask) already shows exactly how to do it. \ No newline at end of file