shynixn / mccoroutine Goto Github PK
View Code? Open in Web Editor NEWMCCoroutine is a library, which adds extensive support for Kotlin Coroutines for Minecraft Server environments.
License: Other
MCCoroutine is a library, which adds extensive support for Kotlin Coroutines for Minecraft Server environments.
License: Other
It is often the case, that we want to measure performance using spigot timings. https://timings.spigotmc.org/
However, coroutines schedules using plugin.launch {}
often yield a name like CancellableContinuationImpl(Single)
which is not helpful if we are using many coroutines spread across larger plugins. It should be possible to use class names in the same way as the standard bukkit scheduler.
Hey there! I'm a huge fan of your library! I use it in my plugin TicketManager, and it's now an integral part of the plugin! Currently my plugin is written for both Paper and Spigot, but I eventually want to create a Sponge version too. However, I noticed that the library is written for Sponge API-7. Do you have any plans to write an MCCoroutine version for Sponge API-8?
Welcome, this issue contains all information about the Hacktoberfest event and available tasks on this repository. Please notice that more tasks may be added later this month.
Required Knowledge
Optional Knowledge
Code/Style/Architecture
master
branch when you are done to initiate a reviewing process❗ It is highly recommend to create a new issue on this project with the task name for example "Refactor convertChatColors" to avoid duplicate task pull requests. ❗
Yes, but only 1 task at a time. After your first pull request has been approved, you can take the second task.
I normally check daily for new activity on this repository.
Just message me below this issue.
~
Hi, you recently implemented MCCoroutines for Minestom and I'd like to firstly thank you for that.
But there is a little issue on the implementation of server.launch { }
: in Minestom, we usually don't store any MinecraftServer
instance because every method in that class is static. That means that instead of server.launch { }
, we should be able to do MinecraftServer.launch { }
.
Unfortunately, MinecraftServer
is a java class and doesn't have any companion object, so making an extension function for the companion object is impossible. A workaround would be to change fun MinecraftServer.launch
to just fun minecraftServerLaunch
.
For now, I am using this:
private var minecraftServer = MinecraftServer()
fun minecraftServerLaunch(
context: CoroutineContext = minecraftServer.minecraftDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job = minecraftServer.launch(context, start, block)
In my JavPlugin#onDisable method, I would like to save the data of a player to a database. However when I try to do that, I get this error:
java.lang.RuntimeException: Plugin wool-wars attempt to start a new coroutine session while being disabled. Dispatchers such as plugin.minecraftDispatcher and plugin.asyncDispatcher are already disposed at this point and cannot be used!
I think it would be great if the coroutine session was only closed after the onDisable method was called, so we could do things like I described earlier.
The SuspendingTabCompleter
doesn't have the same level of support as the regular TabCompleter
provided in Paper.
You can return null
to the TabCompleter
to allow it to fall back to the default command executor but the signature for SuspendingTabCompleter.onTabComplete
is:
suspend fun onTabComplete(
sender: CommandSender,
command: Command,
alias: String,
args: Array<out String>
): List<String>
compared to TabCompleter.onTabComplete
@Nullable
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args);
I checked EventServiceImpl in bukkit-core
and theres different between check and if code
am i wrong or is code wrong?
Would it be possible to add a suspend method for the registering functional-style listener?
An example of the non suspend method: https://docs.papermc.io/velocity/dev/event-api#registering-a-functional-style-listener
Source code:
I hope I provided enough information for you to work with.
Currently, for Velocity, the class SuspendingSimpleCommand
is present. However, lot of server use Brigadier command system.
So, could you add some extensions to use brigadier command in a coroutine context ?
The documentation about brigadier + Example
Other example of brigadier
I am using events to manage my players' prefixes. However, if multiple prefixes are added in the events, ordering is very important.
In my situation, I am adding the player's rank first, then a color after it.
// Lowest priority - rank should be added before other prefixes.
@EventHandler(priority = EventPriority.LOWEST)
suspend fun rankOnPlayerPrefixUpdate(event: PlayerPrefixUpdateEvent) {
delay(500.milliseconds) // Fetch rank from database
event.prefixes.add(Component.text("[Rank] "))
}
// Highest priority - color should be added after other prefixes.
@EventHandler(priority = EventPriority.HIGHEST)
fun rankOnPlayerPrefixUpdate(event: PlayerPrefixUpdateEvent) {
event.prefixes.add(Component.text("[Color] "))
}
// Update player's prefix with an event.
val event = PlayerPrefixUpdateEvent(player, mutableListOf())
server.pluginManager.callSuspendingEvent(event, this).joinAll()
println(event.prefixes) // '[Color] [Rank] '
Even though the rank event is being called first, its prefix is appended last because of the delay. There should be an option in callSuspendingEvent
to wait for each suspending event before continuing to the next one.
Workarounds:
runBlocking
fixes the ordering issue but misuses coroutines.add(0, ...)
to explicitly prepend the prefix works, but is messy and only fixes this specific case.I ran into an issue where I need to do some work with a function like this:
fun Audience.susMsg(message: Component) = plugin.launch {}
I can't because Velocity plugins don't have any sort of superclasses.
Exceptions are captured and logged into console, requiring a handler to be added to the logger. Allow Sentry to capture these exceptions by adding a configuration value which will not handle these exceptions if set to true.
I am using Paper 1.18.2.
I would love an option to do something like this:
doSomething()
delayTicks(5)
doSomething()
I was able to implement this myself like so:
inline val Int.ticks get() = (this * 50).toDuration(DurationUnit.MILLISECONDS)
// Usage
delay(5.ticks)
It appears to work fine for durations longer than one tick, but unfortunately, using 1.tick
results in a gap of two server ticks rather than just one. (I measured this by logging world.fullTime
before and after the delay.)
I'm guessing this has something to do with the inner workings of the library or coroutines that are beyond my understanding.
I think it would be great if something like this were implemented in MCCoroutine-- with my bug fixed, of course ;)
Alternatively, I would love to hear reasoning on why I should avoid measuring with ticks and should instead measure with seconds, if that is better design-wise here. If that is the case, I would be happy to contribute that information to the documentation.
Can you add support for Minestom game server ?
Please, add bungeecord support
Hello,
I'm experiencing the following error:
Caused by: java.lang.ClassNotFoundException: com.github.shynixn.mccoroutine.bukkit.impl.MCCoroutineImpl
I can't also can't find this class in the shaded jar. Even though other classes of this library are available.
Maven pom.xml:
<dependency> <groupId>net.citizensnpcs</groupId> <artifactId>citizens-main</artifactId> <version>2.0.31-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.github.shynixn.mccoroutine</groupId> <artifactId>mccoroutine-bukkit-core</artifactId> <version>2.13.0</version> </dependency>
Maven shade output:
[INFO] Including com.github.shynixn.mccoroutine:mccoroutine-bukkit-api:jar:2.13.0 in the shaded jar. [INFO] Including com.github.shynixn.mccoroutine:mccoroutine-bukkit-core:jar:2.13.0 in the shaded jar.
Am I doing something wrong?
Consider adding support for Velocity.
https://github.com/PaperMC/Velocity
When I use asycnDispatcher it behaves like Dispatchers.Unconfined
in every way, unless it is the main thread. Because of this, I caught a lot of errors, because I thought that he would always switch thread to the asynchronous bukkit, but it continued to execute in the lettuce thread, for example
Dispatch method from com.github.shynixn.mccoroutine.bukkit.dispatcher.AsyncCoroutineDispatcher
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
wakeUpBlockService.ensureWakeup()
return plugin.server.isPrimaryThread
}
[15:22:46 ERROR]: Exception in thread "main" java.lang.RuntimeException: Exception while trying to handle coroutine exception
[15:22:46 ERROR]: at kotlinx.coroutines.CoroutineExceptionHandlerKt.handlerException(CoroutineExceptionHandler.kt:38)
[15:22:46 ERROR]: at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:29)
[15:22:46 ERROR]: at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:196)
[15:22:46 ERROR]: at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:229)
[15:22:46 ERROR]: at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
[15:22:46 ERROR]: at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
[15:22:46 ERROR]: at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
[15:22:46 ERROR]: at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
[15:22:46 ERROR]: at kotlinx.coroutines.intrinsics.CancellableKt.dispatcherFailure(Cancellable.kt:65)
[15:22:46 ERROR]: at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:78)
[15:22:46 ERROR]: at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
[15:22:46 ERROR]: at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
[15:22:46 ERROR]: at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
[15:22:46 ERROR]: at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
[15:22:46 ERROR]: at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
[15:22:46 ERROR]: at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
[15:22:46 ERROR]: at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
[15:22:46 ERROR]: at com.github.shynixn.mccoroutine.velocity.impl.MCCoroutineImpl.setupCoroutineSession(MCCoroutineImpl.kt:52)
[15:22:46 ERROR]: at com.github.shynixn.mccoroutine.velocity.SuspendingPluginContainer.initialize(SuspendingPluginContainer.kt:44)
[15:22:46 ERROR]: at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin.<init>(AstroIslesProxy.kt:42)
[15:22:46 ERROR]: at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin$$FastClassByGuice$$1948344.GUICE$TRAMPOLINE(<generated>)
[15:22:46 ERROR]: at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin$$FastClassByGuice$$1948344.apply(<generated>)
[15:22:46 ERROR]: at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82)
[15:22:46 ERROR]: at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
[15:22:46 ERROR]: at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
[15:22:46 ERROR]: at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
[15:22:46 ERROR]: at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
[15:22:46 ERROR]: at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:169)
[15:22:46 ERROR]: at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:45)
[15:22:46 ERROR]: at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1101)
[15:22:46 ERROR]: at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1139)
[15:22:46 ERROR]: at com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader.createPlugin(JavaPluginLoader.java:124)
[15:22:46 ERROR]: at com.velocitypowered.proxy.plugin.VelocityPluginManager.loadPlugins(VelocityPluginManager.java:155)
[15:22:46 ERROR]: at com.velocitypowered.proxy.VelocityServer.loadPlugins(VelocityServer.java:345)
[15:22:46 ERROR]: at com.velocitypowered.proxy.VelocityServer.start(VelocityServer.java:227)
[15:22:46 ERROR]: at com.velocitypowered.proxy.Velocity.main(Velocity.java:69)
[15:22:46 ERROR]: Suppressed: java.util.NoSuchElementException: No value present
[15:22:46 ERROR]: at java.base/java.util.Optional.get(Optional.java:143)
[15:22:46 ERROR]: at com.velocitypowered.proxy.scheduler.VelocityScheduler$TaskBuilderImpl.schedule(VelocityScheduler.java:197)
[15:22:46 ERROR]: at com.github.shynixn.mccoroutine.velocity.dispatcher.VelocityCoroutineDispatcher.dispatch(VelocityCoroutineDispatcher.kt:28)
[15:22:46 ERROR]: at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
[15:22:46 ERROR]: at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
[15:22:46 ERROR]: ... 26 more
[15:22:46 ERROR]: Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [com.github.shynixn.mccoroutine.velocity.impl.CoroutineSessionImpl$special$$inlined$CoroutineExceptionHandler$1@74ea46e2, StandaloneCoroutine{Cancelling}@48b4a043, VelocityCoroutineDispatcher@2f5b8250]
[15:22:46 ERROR]: Caused by: java.util.NoSuchElementException: No value present
[15:22:46 ERROR]: at java.base/java.util.Optional.get(Optional.java:143)
[15:22:46 ERROR]: at com.velocitypowered.proxy.scheduler.VelocityScheduler$TaskBuilderImpl.schedule(VelocityScheduler.java:197)
[15:22:46 ERROR]: at com.github.shynixn.mccoroutine.velocity.impl.CoroutineSessionImpl$special$$inlined$CoroutineExceptionHandler$1.handleException(CoroutineExceptionHandler.kt:115)
[15:22:46 ERROR]: at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:25)
[15:22:46 ERROR]: ... 34 more
[15:22:46 ERROR]: Can't create plugin mccoroutinesample
com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) [Guice/ErrorInjectingConstructor]: NoSuchElementException: No value present
at MCCoroutineSamplePlugin.<init>(AstroIslesProxy.kt:41)
at VelocityPluginModule.configure(VelocityPluginModule.java:49)
while locating MCCoroutineSamplePlugin
Learn more:
https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR
1 error
======================
Full classname legend:
======================
MCCoroutineSamplePlugin: "com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin"
VelocityPluginModule: "com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule"
========================
End of classname legend:
========================
at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:251) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1104) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1139) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader.createPlugin(JavaPluginLoader.java:124) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.velocitypowered.proxy.plugin.VelocityPluginManager.loadPlugins(VelocityPluginManager.java:155) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.velocitypowered.proxy.VelocityServer.loadPlugins(VelocityServer.java:345) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.velocitypowered.proxy.VelocityServer.start(VelocityServer.java:227) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.velocitypowered.proxy.Velocity.main(Velocity.java:69) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
Caused by: java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:143) ~[?:?]
at com.velocitypowered.proxy.scheduler.VelocityScheduler$TaskBuilderImpl.schedule(VelocityScheduler.java:197) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.github.shynixn.mccoroutine.velocity.dispatcher.VelocityCoroutineDispatcher.dispatch(VelocityCoroutineDispatcher.kt:28) ~[?:?]
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322) ~[?:?]
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[?:?]
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[?:?]
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[?:?]
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[?:?]
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) ~[?:?]
at kotlinx.coroutines.BuildersKt.launch(Unknown Source) ~[?:?]
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47) ~[?:?]
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source) ~[?:?]
at com.github.shynixn.mccoroutine.velocity.impl.MCCoroutineImpl.setupCoroutineSession(MCCoroutineImpl.kt:52) ~[?:?]
at com.github.shynixn.mccoroutine.velocity.SuspendingPluginContainer.initialize(SuspendingPluginContainer.kt:44) ~[?:?]
at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin.<init>(AstroIslesProxy.kt:42) ~[?:?]
at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin$$FastClassByGuice$$1948344.GUICE$TRAMPOLINE(<generated>) ~[?:?]
at com.github.jsh32.astroisles.proxy.MCCoroutineSamplePlugin$$FastClassByGuice$$1948344.apply(<generated>) ~[?:?]
at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:169) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:45) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1101) ~[velocity-3.2.0-SNAPSHOT-256.jar:3.2.0-SNAPSHOT (git-40b76c63-b256)]
... 6 more
The issue is caused by suspendingPluginContainer.initialize(this)
Hi, this is my first time working with coroutines.
I have a service that accesses the database. The service is called when the player logs in.
I marked the method in the service as suspend, to use it in the Listener I just need to make a launch { service.callMethod(arg)
}?
Do I need to mark the listener method as suspend in this case and register accordingly?
I'm not using this library directly, but am following the patterns you described in your initial README before this became an actual library.
This is my Minecraft dispatcher:
private class MinecraftCoroutineDispatcher : CoroutineDispatcher() {
/** Dispatches the coroutine on the main server thread. */
override fun dispatch(context: CoroutineContext, block: Runnable) {
if (Bukkit.isPrimaryThread()) {
block.run()
} else {
// this directly calls the bukkit scheduler internally, no magic here
BukkitExecutor.schedule(action = block::run)
}
}
}
/** A coroutine dispatcher that is confined to the Minecraft server thread. */
public val Dispatchers.Minecraft: CoroutineContext by lazy { MinecraftCoroutineDispatcher() }
This is all nice and dandy, and harnessing the power of Coroutines is truly a joy!
Unfortunately, there's a deadlock situation that I can't figure out how to fix.
Imagine the following code:
runBlocking(Dispatchers.Minecraft) {
// do something on the MC thread...
withContext(Dispatchers.IO) {
// some file I/O
}
// handle the result on the MC thread
}
This will deadlock when switching context back to Dispatchers.Minecraft
, as the Bukkit thread is blocked (thanks to runBlocking
), which means that the Bukkit scheduler will never get to tick and execute the Runnable
.
Unfortunately, in some cases, writing code like this is the most idiomatic thing to do - in my plugin's onEnable
, I want to wait for the execution of a suspending function that switches between the I/O and Minecraft dispatcher a few times. However, the switching will deadlock if we use runBlocking
for the aforementioned reasons.
Do you know of a solution to this issue? Is there a way to detect whether the primary thread is blocked, allowing us to execute code without fear of concurrency issues?
Thanks in advance!
How does it work with canceling events and still maintain async?
As Folia does not provide any "main" minecraft thread, it becomes difficult how to handle threading in Folia based plugins.
Of course, you could handle all things asynchronously but sometimes you have to make sure only 1 thread is accessing a resource at a time.
For example, for minigame plugins you may have a list of List<Arena>
containing the meta data of your created arenas and a List<Game>
containing the active running games on your. You could convert all those collections to concurrency save collections but there may be even more cases where you have to make sure to synchronize access.
There is the globalRegionDispatcher
which could be used for that but then all optimizations of folia may get lost again if all plugins perform most of their work on it. MCCoroutine proposes that each plugin gets their own "main thread" and corresponding "mainDispatcher" for the plugins. It is intended to execute all the stuff the plugin is going to do and send it to the corresponding region thread when the actual minecraft worlds needs to be manipulated.
Example:
plugin.launch(plugin.mainDispatcher) {
val game = getGameByName("myfirstgame")
game.join(player)
withContext(plugin.entityDispatcher(player)) {
player.teleport(game.lobbySpawnpoint)
player.sendMessage("You have joined the game")
}
if(game.hasEnoughPlayers()){
game.start()
}
}
This will be the default parameter in Folia going forward. So simply calling plugin.launch {}
schedules something on the "plugin main thread."
Notes:
runBlocking
in onDisable
.How to fix:
plugin.isEnabled
check to isDispatchNeeded
in com.github.shynixn.mccoroutine.bukkit.dispatcher.MinecraftCoroutineDispatcher
and com.github.shynixn.mccoroutine.bukkit.dispatcher.AsyncCoroutineDispatcher
if (!plugin.isEnabled)
check in dispatch
from those filesI'm testing out coroutines by routing all events to a shared flow. I'm getting the following error on /reload
:
Caused by: java.util.zip.ZipException: ZipFile invalid LOC header (bad signature)
at java.util.zip.ZipFile$ZipFileInputStream.initDataOffset(ZipFile.java:1003) ~[?:?]
at java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:1013) ~[?:?]
at java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:468) ~[?:?]
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:159) ~[?:?]
at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[?:?]
at com.google.common.io.ByteStreams.copy(ByteStreams.java:106) ~[spigot-1.16.5.jar:3016-Spigot-73fb609-ea050e6]
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:166) ~[spigot-1.16.5.jar:3016-Spigot-73fb609-ea050e6]
at org.bukkit.plugin.java.PluginClassLoader.findClass(PluginClassLoader.java:135) ~[spigot-1.16.5.jar:3016-Spigot-73fb609-ea050e6]
at org.bukkit.plugin.java.PluginClassLoader.findClass(PluginClassLoader.java:96) ~[spigot-1.16.5.jar:3016-Spigot-73fb609-ea050e6]
at java.lang.ClassLoader.loadClass(ClassLoader.java:589) ~[?:?]
at java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[?:?]
at kotlinx.coroutines.JobSupport.getChildren(JobSupport.kt:947) ~[?:?]
at kotlinx.coroutines.JobKt__JobKt.cancelChildren(Job.kt:628) ~[?:?]
at kotlinx.coroutines.JobKt.cancelChildren(Unknown Source) ~[?:?]
at kotlinx.coroutines.JobKt__JobKt.cancelChildren$default(Job.kt:627) ~[?:?]
at kotlinx.coroutines.JobKt.cancelChildren$default(Unknown Source) ~[?:?]
at com.github.shynixn.mccoroutine.service.CoroutineSessionImpl.dispose(CoroutineSessionImpl.kt:57) ~[?:?]
at com.github.shynixn.mccoroutine.impl.MCCoroutineImpl.disable(MCCoroutineImpl.kt:32) ~[?:?]
at com.github.shynixn.mccoroutine.listener.PluginListener.onPluginDisable(PluginListener.kt:19) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:315) ~[spigot-1.16.5.jar:3016-Spigot-73fb609-ea050e6]
Perhaps this is related to how the scope is disposed? Or more specifically, usage of Plugin.findClazz
This article is translated by Google
When using mc-coroutine on my paper server
It crashed (plugin activation failed
After viewing the error message
I think a field called pluginParentTimer is causing this problem
This field exists in org.bukkit.plugin.java.JavaPluginLoader
Let's take a look at the implementation of JavaPluginLoader on spigot
public final class JavaPluginLoader implements PluginLoader {
final Server server;
private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
private final List<PluginClassLoader> loaders = new CopyOnWriteArrayList<PluginClassLoader>();
public static final CustomTimingsHandler pluginParentTimer = new CustomTimingsHandler("** Plugins"); // Spigot
}
on paper
public final class JavaPluginLoader implements PluginLoader {
final Server server;
private static final boolean DISABLE_CLASS_PRIORITIZATION = Boolean.getBoolean("Paper.DisableClassPrioritization"); // Paper
private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
private final Map<String, java.util.concurrent.locks.ReentrantReadWriteLock> classLoadLock = new java.util.HashMap<String, java.util.concurrent.locks.ReentrantReadWriteLock>(); // Paper
private final Map<String, Integer> classLoadLockCount = new java.util.HashMap<String, Integer>(); // Paper
private final List<PluginClassLoader> loaders = new CopyOnWriteArrayList<PluginClassLoader>();
}
So JavaPluginLoader.pluginParentTimer is only defined on spigot
But JavaPluginLoader.pluginParentTimer is used in line 134 in mccoroutine-bukkit-core/src/main/java/com/github/shynixn/mccoroutine/service/EventServiceImpl.kt
Hi,
To describe my issue, let's take a look on the following event handler:
@EventHandler(ignoreCancelled = false)
suspend fun onBlockBreak(e: BlockBreakEvent) {
val blockMaterial = e.block.type // Assigning blockMaterial here, as it gets erased after calling a suspending function
withContext(Dispatchers.IO) {
delay(10)
}
if (blockMaterial == Material.STONE) {
e.isDropItems = false // Does not stop items drop
e.isCancelled = true // Does not cancel the event
}
}
After calling a suspending function - withContext(Dispatchers.IO
delay(10)
in this case - the event gets "erased/reset".
My question is: How can I keep the access to the event after calling a suspending function?
It'd be nice if someone could explain me why the event gets erased, too.
MCCoroutineException event may cause an exception in 2.6.0. See #84.
If you encounter it, temporarily use MCCoroutine 2.5.0 until the next version is released.
PluginManager.callSuspendingEvent(....)
This code prints Coroutine has been cancelled.
after a second.
val job = launch { // Plugin.launch
delay(1000.seconds)
}
launch { // Plugin.launch
delay(1.seconds)
job.cancel()
}
However, this code does not.
launch { // Plugin.launch
val job = launch { // CoroutineScope.launch
delay(1000.seconds)
}
delay(1.seconds)
job.cancel()
}
Is this how you are supposed to make a cancellable coroutine?
здравствуйте, увидел ваш плагин на spigot, и заметил там такую штуку как имитирование async,await в Bukkit, и вот хотел бы узнать, это ошибка или нет
так то все работает в плагине на гибритке только что выстакивают две вот такие ошибки хотя я юзаю ваш плагин.
[21:36:24 WARN]: Legacy plugin MCCoroutine-Sample v2.15.0 does not specify an api-version.
[21:36:25 INFO]: [Typewriter] Loading 4 libraries... please wait
[21:36:25 INFO]: [Typewriter] Loading 18 extra libraries... please wait
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.7.1/kotlinx-coroutines-core-1.7.1.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/annotations/13.0/annotations-13.0.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-codec/4.1.63.Final/netty-codec-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/com/corundumstudio/socketio/netty-socketio/1.7.19/netty-socketio-1.7.19.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-buffer/4.1.63.Final/netty-buffer-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-handler/4.1.63.Final/netty-handler-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.20/kotlin-stdlib-jdk8-1.8.20.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.20/kotlin-stdlib-jdk7-1.8.20.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-resolver/4.1.63.Final/netty-resolver-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/com/fasterxml/jackson/core/jackson-core/2.12.3/jackson-core-2.12.3.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/com/fasterxml/jackson/core/jackson-databind/2.12.3/jackson-databind-2.12.3.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlin/kotlin-reflect/1.9.22/kotlin-reflect-1.9.22.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-transport-native-epoll/4.1.63.Final/netty-transport-native-epoll-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.20/kotlin-stdlib-common-1.8.20.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-transport-native-unix-common/4.1.63.Final/netty-transport-native-unix-common-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-transport/4.1.63.Final/netty-transport-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-common/4.1.63.Final/netty-common-4.1.63.Final.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.1/kotlinx-coroutines-core-jvm-1.7.1.jar
[21:36:25 INFO]: [Typewriter] Loaded libraries libraries/plugins-lib/io/netty/netty-codec-http/4.1.63.Final/netty-codec-http-4.1.63.Final.jar
[21:36:26 INFO]: [MCCoroutine-Sample] Loading MCCoroutine-Sample v2.15.0
[21:36:26 INFO]: [ProtocolLib] Loading ProtocolLib v5.2.0-SNAPSHOT-679
[21:36:30 INFO]: [packetevents] Loading packetevents v2.2.1
[21:36:31 INFO]: [Typewriter] Loading Typewriter v0.4.2
[21:36:32 INFO]: [Typewriter] Loading adapter BasicAdapter
[21:36:36 WARN]: [Typewriter] Regex annotation can only be used on strings (including in lists or maps)!
[21:36:36 ERROR]: io/papermc/paper/event/player/AsyncChatEvent initializing Typewriter v0.4.2 (Is it up to date?)
java.lang.NoClassDefFoundError: io/papermc/paper/event/player/AsyncChatEvent
at com.mohistmc.bukkit.remapping.ClassLoaderRemapper.tryDefineClass(ClassLoaderRemapper.java:173) ~[forge-1.20.1-47.2.23-universal.jar%23201!/:?]
at com.mohistmc.bukkit.remapping.ReflectionHandler_3.redirectGetMethods(ReflectionHandler.java:61) ~[forge-1.20.1-47.2.23-universal.jar%23201!/:?]
at me.gabber235.typewriter.adapters.AdapterListeners$constructAdapterListeners$1.invoke(AdapterListeners.kt:77) ~[?:?]
at me.gabber235.typewriter.adapters.AdapterListeners$constructAdapterListeners$1.invoke(AdapterListeners.kt:77) ~[?:?]
at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:315) ~[?:?]
at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303) ~[?:?]
at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:169) ~[?:?]
at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194) ~[?:?]
at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:169) ~[?:?]
at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194) ~[?:?]
at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:214) ~[?:?]
at kotlin.sequences.SequencesKt___SequencesKt.toList(Sequences.kt:816) ~[?:?]
at me.gabber235.typewriter.adapters.AdapterListeners.constructAdapterListeners(AdapterListeners.kt:81) ~[?:?]
at me.gabber235.typewriter.adapters.AdapterLoaderImpl.constructAdapter(AdapterLoader.kt:176) ~[?:?]
at me.gabber235.typewriter.adapters.AdapterLoaderImpl.loadAdapter(AdapterLoader.kt:156) ~[?:?]
at me.gabber235.typewriter.adapters.AdapterLoaderImpl.loadAdapters(AdapterLoader.kt:75) ~[?:?]
at me.gabber235.typewriter.Typewriter.onLoad(Typewriter.kt:80) ~[?:?]
at org.bukkit.craftbukkit.v1_20_R1.CraftServer.loadPlugins(CraftServer.java:441) ~[forge-1.20.1-47.2.23-universal.jar%23201!/:997de31d-ddc9a2dad-d2eba2c8-47.1.79]
at net.minecraft.server.dedicated.DedicatedServer.m_7038(DedicatedServer.java:216) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:848) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.MinecraftServer.m_206580_(MinecraftServer.java:281) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at java.lang.Thread.run(Thread.java:840) [?:?]
[21:36:46 INFO]: [MCCoroutine-Sample] Enabling MCCoroutine-Sample v2.15.0
[21:36:46 INFO]: [MCCoroutineSamplePlugin/onEnableAsync] Is starting on Thread:Server thread/49/primaryThread=true
[21:36:46 INFO]: [MCCoroutineSamplePlugin/onEnableAsync] Simulating data load Thread:DefaultDispatcher-worker-1/65/primaryThread=false
[21:36:46 INFO]: [MCCoroutineSamplePlugin/onEnableAsync] Is ending on Thread:Server thread/49/primaryThread=true
[21:36:46 INFO]: [packetevents] Enabling packetevents v2.2.1
[21:36:46 INFO]: [packetevents] Checking for updates, please wait...
[21:36:46 INFO]: [Typewriter] Enabling Typewriter v0.4.2
[21:36:47 INFO]: [packetevents] You are on the latest released version of packetevents. Your build: (2.2.1)
[21:36:47 INFO]: [Mohist] Enabling Mohist v1.20.1
[21:36:47 INFO]: Server permissions file permissions.yml is empty, ignoring it
[21:36:47 INFO]: Done (11.264s)! For help, type "help"
container@nookure~ Server marked as running...
[21:36:47 INFO]: Successfully initialized permission handler forge:default_handler
[21:36:47 INFO]: Forwarding forge permission[forge:default_handler] to bukkit[mohist:permission]
[21:36:47 INFO]: [Typewriter] Loaded 0 entries from 0 pages.
[21:36:53 ERROR]: [Typewriter] This is not an error of MCCoroutine! See sub exception for details.
java.lang.NoSuchMethodError: 'io.papermc.paper.plugin.configuration.PluginMeta org.bukkit.plugin.Plugin.getPluginMeta()'
at me.gabber235.typewriter.extensions.modrinth.Modrinth.findLatestVersion(Modrinth.kt:77) ~[?:?]
at me.gabber235.typewriter.extensions.modrinth.Modrinth.loadVersion(Modrinth.kt:56) ~[?:?]
at me.gabber235.typewriter.extensions.modrinth.Modrinth.access$loadVersion(Modrinth.kt:23) ~[?:?]
at me.gabber235.typewriter.extensions.modrinth.Modrinth$loadVersion$1.invokeSuspend(Modrinth.kt) ~[?:?]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[?:?]
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) ~[?:?]
at org.bukkit.craftbukkit.v1_20_R1.scheduler.CraftTask.run(CraftTask.java:78) ~[forge-1.20.1-47.2.23-universal.jar%23201!/:?]
at org.bukkit.craftbukkit.v1_20_R1.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:416) ~[forge-1.20.1-47.2.23-universal.jar%23201!/:?]
at net.minecraft.server.MinecraftServer.m_5703_(MinecraftServer.java:1133) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.dedicated.DedicatedServer.m_5703_(DedicatedServer.java:346) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:1072) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:902) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at net.minecraft.server.MinecraftServer.m_206580_(MinecraftServer.java:281) ~[server-1.20.1-20230612.114412-srg.jar%23196!/:?]
at java.lang.Thread.run(Thread.java:840) [?:?]
onEnable, onReload, onDisable often does not got used correctly with MCCoroutine, therefore an implementation should be provided
How can i dispatch without scheduler in primary-thread?
For example
inline fun<reified T: Event> Player.listenAsync(withCancel: Boolean = true): Deferred<T> {
val completableDeferred = CompletableDeferred<T>()
ListeningPlayer.register(T::class.java, this) {
completableDeferred.complete(it)
}
return completableDeferred
}
then if i use await with it
it just run after next tick
I don't see what's done differently with your build gradle, but it seems like sources are not bundled together in through maven. When navigating the code, IntelliJ uses decompiled .class
files rather than actual sources, so users won't be able to navigate through the code.
So this is a bit of a strange issue I've encountered; when using a Suspending Listener (A listener registered with registerSuspendingEvents()), multiple Exceptions are thrown when listening to certain events and then are caught somewhere such that they don't appear in the server's log. The only way I was able to catch onto this was by analysing Spark's profiler and seeing that significant CPU time was being taken by jdk.internal.reflect.GeneratedMethodAccessor.invoke()
. After additional testing I was eventually able to get logs which pointed to event listeners. I tracked it to the ChunkLoadEvent and PhysicsEvent.
(For the record, I am listening to these events with a SuspendingListener. I tested with and without the suspend keyword on the event listener functions but still encountered the issue)
From guess work I used a normal Listener interface and the Exceptions were now gone and so was the extra CPU time consumed by them.
The plugin itself is a custom plugin that when loading chunks will scan each chunk and based on certain block and biome predicates replace an above-ground block with a custom vegetation (a custom model that drops unique drops). There is a lot of concurrency involved is the main point.
Spark Profiler - USING Suspending Listener
https://spark.lucko.me/EkXjQTa7Vp
Spark Profiler - USING Normal Listener
https://spark.lucko.me/1UJTdh3uGz
Since MCCoroutine uses the normal bukkit scheduler, it seems to struggle with stuff on Folia
Any chance this can get supported?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.