Skip to content

Commit fea39be

Browse files
authored
Merge pull request #114 from Shynixn/development
Merge changes to master --release
2 parents ca3dd7f + c120f64 commit fea39be

File tree

21 files changed

+188
-86
lines changed

21 files changed

+188
-86
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ tasks.register("printVersion") {
4343

4444
subprojects {
4545
group 'com.github.shynixn.mccoroutine'
46-
version '2.15.0'
46+
version '2.16.0'
4747

4848
sourceCompatibility = 1.8
4949

docs/wiki/docs/commandexecutor.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,22 @@ plugins.
9696

9797
class PlayerDataCommandExecutor(private val database: Database) : SuspendingCommandExecutor {
9898
override suspend fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
99+
// In Folia, this will be the global region thread, or entity execution thread.
100+
// In Bukkit, this will be the main thread.
101+
99102
if (sender !is Player) {
100103
return false
101104
}
102105

103106
if (args.size == 2 && args[0].equals("rename", true)) {
104107
val name = args[1]
105-
val playerData = database.getDataFromPlayer(sender)
106-
playerData.name = name
107-
database.saveData(sender, playerData)
108+
withContext(plugin.mainDispatcher) {
109+
// Make sure you switch to your plugin main thread before you do anything in your plugin.
110+
val playerData = database.getDataFromPlayer(sender)
111+
playerData.name = name
112+
database.saveData(sender, playerData)
113+
}
114+
108115
return true
109116
}
110117

@@ -312,14 +319,14 @@ plugins.
312319
private val database = Database()
313320

314321
override suspend fun onEnableAsync() {
315-
// Minecraft Main Thread
322+
// Global Region Thread.
316323
database.createDbIfNotExist()
317324
server.pluginManager.registerSuspendingEvents(PlayerDataListener(database), this)
318325
getCommand("playerdata")!!.setSuspendingExecutor(PlayerDataCommandExecutor(database))
319326
}
320327

321328
override suspend fun onDisableAsync() {
322-
// Minecraft Main Thread
329+
// Global Region Thread.
323330
}
324331
}
325332
````

docs/wiki/docs/coroutine.md

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
# Kotlin Coroutines and Minecraft Plugins
22

3-
When starting with [Coroutines in Kotlin](https://kotlinlang.org/docs/coroutines-basics.html), it is interesting
4-
how this can be translated to the world of minecraft plugins. It is recommended to learn how Kotlin Coroutines work
5-
before you continue here.
3+
When starting with [Coroutines in Kotlin](https://kotlinlang.org/docs/coroutines-basics.html), you may wonder how
4+
you can use them for minecraft plugins and mods. This guide introduces concepts and a production ready API you can use, to start
5+
adding coroutines to your project.
66

77
!!! note "Important"
88
Make sure you have already installed MCCoroutine. See [Installation](/gettingstarted) for details.
99

1010
### Starting a coroutine
1111

12-
In order to start coroutine You may also encounter the function
13-
``runBlocking`` because it makes sense for certain scenarios such as unittest.
14-
However, keep in mind to **avoid** using ``runblocking`` in any of your plugins.
12+
In order to start a coroutine, you can use the provided ``plugin.launch {}`` extension method. This is safe to be called
13+
anywhere in your plugin except in onDisable where you need to use ``runBlocking``. However, keep in mind to **avoid** using ``runblocking`` anywhere else in any of your plugins.
1514

1615
* To enter a coroutine **anywhere** in your code at any time:
1716

@@ -62,29 +61,45 @@ However, keep in mind to **avoid** using ``runblocking`` in any of your plugins.
6261

6362
=== "Folia"
6463

65-
As Folia brings multithreading to Paper based servers, threading becomes more complicated and MCCoroutine requires you to think
66-
everytime you call plugin.launch. In Bukkit based servers, MCCoroutine can assume the correct thread automatically and optimise ticking. (e.g.
67-
not sending a task to the scheduler if you are already on the main thread).
68-
69-
In Folia, there are many threadpools (explained below) and we do not have a main thread.
64+
As Folia brings multithreading to Paper based servers, threading becomes a lore more complicated for plugin developers.
7065

7166
!!! note "Important"
7267
You can run mccoroutine-folia in standard Bukkit servers as well. MCCoroutine automatically falls back to the standard Bukkit
7368
scheduler if the Folia schedulers are not found and the rules for mccoroutine-bukkit start to apply.
69+
70+
!!! note "Important"
71+
If you have been using mccoroutine for Bukkit before, you have to perform some restructuring in your plugin. **Simply changing the imports is not enough.**
72+
``plugin.launch {}`` works differently in Folia compared to Bukkit.
73+
74+
First, it is important to understand that Folia does not have a server main thread. In order to access minecraft resources you need to use the correct thread for
75+
a given resource. For an entity, you need to use the currently assigned thread for that entity. MCCoroutine provides dispatchers for each of these usecases and
76+
automatically falls back to the matching dispatchers if you are on a Bukkit server instead of a Folia server.
77+
78+
However, this does not solve the problem of accessing our own data in our plugins. We do not have a main thread, so we could try accessing our data on the incoming
79+
thread. However, sometimes you have to make sure only 1 thread is accessing a resource at a time. This is important for ordering events and avoiding concurrency exceptions.
80+
Concurrent collections can help with that but you may still need synchronize access in other places.
81+
82+
As a solution, MCCoroutine proposes that each plugin gets their own "main thread" and corresponding "mainDispatcher". It is intended to execute all the stuff the plugin is going to do.
83+
For minecraft actions, like teleporting a player or manipulating an entity. You simply excute them in a sub context and return back to your personal main thread. This
84+
concepts result into the following code.
7485

7586
```kotlin
7687
import com.github.shynixn.mccoroutine.folia.launch
7788
import org.bukkit.plugin.Plugin
7889

7990
fun foo(entity : Entity) {
80-
// The plugin.entityDispatcher(entity) parameter ensures, that we end up on the scheduler for the entity in the specific region if we suspend
81-
// inside the plugin.launch scope. (e.g. using delay)
82-
// The CoroutineStart.UNDISPATCHED ensures, that we enter plugin.launch scope without any delay on the current thread.
83-
// You are responsible to ensure that you are on the correct thread pool (in this case the thread pool for the entity), if you pass CoroutineStart.UNDISPATCHED.
84-
// This is automatically the case if you use plugin.launch{} in events or commands. You can simply use CoroutineStart.UNDISPATCHED here.
85-
// If you use CoroutineStart.DEFAULT, the plugin.launch scope is entered in the next scheduler tick.
86-
plugin.launch(plugin.entityDispatcher(entity), CoroutineStart.UNDISPATCHED) {
87-
// In this case this will be the correct thread for the given entity, if the thread was correct before calling plugin.launch.
91+
plugin.launch { // or plugin.launch(plugin.mainDispatcher) {}
92+
// Your plugin main thread. If you have already been on your plugin main thread, this scope is entered immidiatly.
93+
// Regardless if your are on bukkit or on folia, this is your personal thread and you must not call bukkit methods on it.
94+
// Now perform some data access on your plugin data like accessing a repository.
95+
val storedEntityDataInDatabase = database.get()
96+
97+
// Apply the data on the entity thread using the entityDispatcher.
98+
// The plugin.entityDispatcher(entity) parameter ensures, that we end up on the scheduler for the entity in the specific region.
99+
withContext(plugin.entityDispatcher(entity)) {
100+
// In Folia, this will be the correct thread for the given entity.
101+
// In Bukkit, this will be the main thread.
102+
}
88103
}
89104
}
90105
```
@@ -306,7 +321,8 @@ A dispatcher determines what thread or threads the corresponding coroutine uses
306321

307322
In Folia, MCCoroutine offers 4 custom dispatchers.
308323

309-
* globalRegion (Allows to execute coroutines on the global region. e.g. Global Game Rules)
324+
* mainDispatcher (Your personal plugin main thread, allows to execute coroutines on it)
325+
* globalRegionDispatcher (Allows to execute coroutines on the global region. e.g. Global Game Rules)
310326
* regionDispatcher (Allows to execute coroutines on a specific location in a world)
311327
* entityDispatcher (Allows to execute coroutines on a specific entity)
312328
* asyncDispatcher (Allows to execute coroutines on the async thread pool)
@@ -315,16 +331,20 @@ A dispatcher determines what thread or threads the corresponding coroutine uses
315331

316332
```kotlin
317333
fun foo(location: Location)) {
318-
plugin.launch(plugin.regionDispatcher(location), CoroutineStart.UNDISPATCHED) {
319-
// The correct thread for the given location without delay, if the thread was correct before calling plugin.launch.
334+
plugin.launch {
335+
// Always make your you are on your personal plugin main thread.
320336

321-
val result = withContext(Dispatchers.IO) {
322-
// Perform operations asynchronously.
323-
"Playxer is Max"
337+
val resultBlockType = withContext(plugin.regionDispatcher(location)) {
338+
// In Folia, this will be the correct thread for the given location
339+
// In Bukkit, this will be the main thread.
340+
getTypeOfBlock()
341+
}
342+
343+
myBlockTypeList.add(resultBlockType)
344+
345+
withContext(plugin.asyncDispatcher) {
346+
// save myBlockTypeList to file.
324347
}
325-
326-
// The correct thread for the given location.
327-
println(result) // Prints 'Player is Max'
328348
}
329349
}
330350
```

docs/wiki/docs/installation.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,62 +8,62 @@ In order to use the MCCoroutine Kotlin API, you need to include the following li
88

99
```groovy
1010
dependencies {
11-
implementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.15.0")
12-
implementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:2.15.0")
11+
implementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.16.0")
12+
implementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:2.16.0")
1313
}
1414
```
1515

1616
=== "BungeeCord"
1717

1818
```groovy
1919
dependencies {
20-
implementation("com.github.shynixn.mccoroutine:mccoroutine-bungeecord-api:2.15.0")
21-
implementation("com.github.shynixn.mccoroutine:mccoroutine-bungeecord-core:2.15.0")
20+
implementation("com.github.shynixn.mccoroutine:mccoroutine-bungeecord-api:2.16.0")
21+
implementation("com.github.shynixn.mccoroutine:mccoroutine-bungeecord-core:2.16.0")
2222
}
2323
```
2424

2525
=== "Fabric"
2626

2727
```groovy
2828
dependencies {
29-
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-api:2.15.0")
30-
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-core:2.15.0")
29+
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-api:2.16.0")
30+
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-core:2.16.0")
3131
}
3232
```
3333

3434
=== "Folia"
3535

3636
```groovy
3737
dependencies {
38-
implementation("com.github.shynixn.mccoroutine:mccoroutine-folia-api:2.15.0")
39-
implementation("com.github.shynixn.mccoroutine:mccoroutine-folia-core:2.15.0")
38+
implementation("com.github.shynixn.mccoroutine:mccoroutine-folia-api:2.16.0")
39+
implementation("com.github.shynixn.mccoroutine:mccoroutine-folia-core:2.16.0")
4040
}
4141
```
4242

4343
=== "Minestom"
4444

4545
```groovy
4646
dependencies {
47-
implementation("com.github.shynixn.mccoroutine:mccoroutine-minestom-api:2.15.0")
48-
implementation("com.github.shynixn.mccoroutine:mccoroutine-minestom-core:2.15.0")
47+
implementation("com.github.shynixn.mccoroutine:mccoroutine-minestom-api:2.16.0")
48+
implementation("com.github.shynixn.mccoroutine:mccoroutine-minestom-core:2.16.0")
4949
}
5050
```
5151

5252
=== "Sponge"
5353

5454
```groovy
5555
dependencies {
56-
implementation("com.github.shynixn.mccoroutine:mccoroutine-sponge-api:2.15.0")
57-
implementation("com.github.shynixn.mccoroutine:mccoroutine-sponge-core:2.15.0")
56+
implementation("com.github.shynixn.mccoroutine:mccoroutine-sponge-api:2.16.0")
57+
implementation("com.github.shynixn.mccoroutine:mccoroutine-sponge-core:2.16.0")
5858
}
5959
```
6060

6161
=== "Velocity"
6262

6363
```groovy
6464
dependencies {
65-
implementation("com.github.shynixn.mccoroutine:mccoroutine-velocity-api:2.15.0")
66-
implementation("com.github.shynixn.mccoroutine:mccoroutine-velocity-core:2.15.0")
65+
implementation("com.github.shynixn.mccoroutine:mccoroutine-velocity-api:2.16.0")
66+
implementation("com.github.shynixn.mccoroutine:mccoroutine-velocity-core:2.16.0")
6767
}
6868
```
6969

@@ -87,17 +87,17 @@ dependencies {
8787
**plugin.yml**
8888
```yaml
8989
libraries:
90-
- com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.15.0
91-
- com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:2.15.0
90+
- com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:2.16.0
91+
- com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:2.16.0
9292
```
9393

9494
=== "Folia"
9595

9696
**plugin.yml**
9797
```yaml
9898
libraries:
99-
- com.github.shynixn.mccoroutine:mccoroutine-folia-api:2.15.0
100-
- com.github.shynixn.mccoroutine:mccoroutine-folia-core:2.15.0
99+
- com.github.shynixn.mccoroutine:mccoroutine-folia-api:2.16.0
100+
- com.github.shynixn.mccoroutine:mccoroutine-folia-core:2.16.0
101101
```
102102

103103

docs/wiki/docs/listener.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,22 @@ suspendable functions). You can mix suspendable and non suspendable functions in
110110
class PlayerDataListener(private val database: Database) : Listener {
111111
@EventHandler
112112
suspend fun onPlayerJoinEvent(event: PlayerJoinEvent) {
113-
val player = event.player
114-
val playerData = database.getDataFromPlayer(player)
115-
playerData.name = player.name
116-
playerData.lastJoinDate = Date()
117-
database.saveData(player, playerData)
113+
// In Folia, this will be entity thread of the player.
114+
// In Bukkit, this will be the main thread.
115+
withContext(plugin.mainDispatcher) {
116+
// Make sure you switch to your plugin main thread before you do anything in your plugin.
117+
val player = event.player
118+
val playerData = database.getDataFromPlayer(player)
119+
playerData.name = player.name
120+
playerData.lastJoinDate = Date()
121+
database.saveData(player, playerData)
122+
}
118123
}
119124

120125
@EventHandler
121126
fun onPlayerQuitEvent(event: PlayerQuitEvent) {
122-
// Alternative way to achieve the same thing
123-
plugin.launch(plugin.entityDispatcher(event.player)), CoroutineStart.UNDISPATCHED) {
127+
plugin.launch {
128+
// Make sure you switch to your plugin main thread before you do anything in your plugin.
124129
val player = event.player
125130
val playerData = database.getDataFromPlayer(player)
126131
playerData.name = player.name

docs/wiki/docs/unittests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ feedback to the real environment.
1818

1919
```kotlin
2020
dependencies {
21-
testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.15.0")
21+
testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.16.0")
2222
}
2323
```
2424

mccoroutine-bukkit-sample/src/main/resources/plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: MCCoroutine-Sample
2-
version: 2.15.0
2+
version: 2.16.0
33
author: Shynixn
44
main: com.github.shynixn.mccoroutine.bukkit.sample.MCCoroutineSamplePlugin
55
commands:

mccoroutine-bungeecord-sample/src/main/resources/plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: MCCoroutine-Sample
2-
version: 2.15.0
2+
version: 2.16.0
33
author: Shynixn
44
main: com.github.shynixn.mccoroutine.bungeecord.sample.MCCoroutineSamplePlugin
55
commands:

mccoroutine-fabric-sample/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ repositories {
99
mavenLocal()
1010
}
1111
dependencies {
12-
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-api:2.15.0")
13-
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-core:2.15.0")
12+
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-api:2.16.0")
13+
implementation("com.github.shynixn.mccoroutine:mccoroutine-fabric-core:2.16.0")
1414

1515
minecraft("com.mojang", "minecraft", project.extra["minecraft_version"] as String)
1616
mappings("net.fabricmc", "yarn", project.extra["yarn_mappings"] as String, null, "v2")

mccoroutine-folia-api/src/main/java/com/github/shynixn/mccoroutine/folia/CoroutineSession.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ interface CoroutineSession {
2828
*/
2929
val dispatcherAsync: CoroutineContext
3030

31+
/**
32+
* The main dispatcher represents the main thread of a plugin.
33+
*/
34+
val dispatcherMain : CoroutineContext
35+
3136
/**
3237
* Manipulates the bukkit server heart beat on startup.
3338
*/

0 commit comments

Comments
 (0)