Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MAVEN_USERNAME=YOUR_USERNAME
MAVEN_PASSWORD=YOUR_PASSWORD
12 changes: 8 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
id("org.jetbrains.dokka") version "2.1.0"
id("org.jetbrains.dokka-javadoc") version "2.1.0"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19" apply false
id("co.uzzu.dotenv.gradle") version "2.0.0"
}

private val submodules: HashMap<String, String> = hashMapOf(
Expand Down Expand Up @@ -50,6 +51,9 @@ val packageSources by tasks.registering(Jar::class) {
from(subprojects.filter { "kotlin" !in it.path }.map { it.sourceSets.main.get().allSource })
}

val maven_username = if (env.isPresent("MAVEN_USERNAME")) env.fetch("MAVEN_USERNAME") else ""
val maven_password = if (env.isPresent("MAVEN_PASSWORD")) env.fetch("MAVEN_PASSWORD") else ""
Comment thread
MrLarkyy marked this conversation as resolved.
Outdated

publishing {
publications {
create<MavenPublication>("main") {
Expand Down Expand Up @@ -98,16 +102,16 @@ publishing {
name = "undefined-releases"
url = uri("https://repo.undefinedcreations.com/releases")
credentials(PasswordCredentials::class) {
username = System.getenv("MAVEN_NAME") ?: property("mavenUser").toString()
password = System.getenv("MAVEN_SECRET") ?: property("mavenPassword").toString()
username = maven_username
password = maven_password
}
}
maven {
name = "undefined-snapshots"
url = uri("https://repo.undefinedcreations.com/snapshots")
credentials(PasswordCredentials::class) {
username = System.getenv("MAVEN_NAME") ?: property("mavenUser").toString()
password = System.getenv("MAVEN_SECRET") ?: property("mavenPassword").toString()
username = maven_username
password = maven_password
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
plugins {
plugins {
Comment thread
MrLarkyy marked this conversation as resolved.
id("setup")
}

Expand Down
322 changes: 240 additions & 82 deletions common/src/main/kotlin/com/undefined/stellar/AbstractStellarCommand.kt

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions common/src/main/kotlin/com/undefined/stellar/BukkitCtx.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.undefined.stellar

import kotlinx.coroutines.*
import org.bukkit.Bukkit
import kotlin.coroutines.CoroutineContext

object BukkitCtx : CoroutineDispatcher() {

val scope by lazy {
CoroutineScope(
this + SupervisorJob() + CoroutineExceptionHandler { _, e ->
StellarConfig.plugin!!.logger.severe("An error occurred while running a task!")
e.printStackTrace()
},
)
}
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !Bukkit.getServer().isPrimaryThread
}

override fun dispatch(context: CoroutineContext, block: Runnable) {
if (isDispatchNeeded(context)) {
Bukkit.getScheduler().runTask(StellarConfig.plugin!!, Runnable {
try {
block.run()
} catch (e: Throwable) {
StellarConfig.plugin!!.logger.severe("An error occurred while running a task!")
e.printStackTrace()
}
})
} else {
block.run()
}
}

fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return scope.launch(block = block)
}

operator fun invoke(block: suspend CoroutineScope.() -> Unit) = launch(block = block)
operator fun invoke() = scope
}
84 changes: 84 additions & 0 deletions common/src/main/kotlin/com/undefined/stellar/StellarConfig.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,99 @@
package com.undefined.stellar

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import net.kyori.adventure.text.minimessage.MiniMessage
import org.bukkit.plugin.java.JavaPlugin
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledThreadPoolExecutor
import kotlin.coroutines.CoroutineContext

object StellarConfig {

@ApiStatus.Internal
var miniMessage: MiniMessage? = null
private set
get() = field ?: MiniMessage.miniMessage()

@ApiStatus.Internal
var prefix: String = ""
private set

@ApiStatus.Internal
var plugin: JavaPlugin? = null
private set

@ApiStatus.Internal
val commands: MutableList<AbstractStellarCommand<*>> = mutableListOf()

private val asyncCtx = object : CoroutineDispatcher() {

private val group = ThreadGroup("Async-Coroutine-Executors")

val executor: ScheduledExecutorService by lazy {
object :
ScheduledThreadPoolExecutor(
8,
Thread.ofPlatform()
.group(group)
.name("Async-Coroutine-Executor-", 0)
.daemon(true)
.uncaughtExceptionHandler { t, e ->
plugin!!.logger.severe("An error occurred while running a task in $t!")
e.printStackTrace()
}
.factory(),
) {

override fun afterExecute(r: kotlinx.coroutines.Runnable?, t: Throwable?) {
if (t != null) {
plugin!!.logger.severe("An error occurred while running a task $r!")
t.printStackTrace()
}
}
}
}


val scope by lazy {
CoroutineScope(
this + SupervisorJob() + CoroutineExceptionHandler { _, e ->
plugin!!.logger.severe("An error occurred while running a task!")
e.printStackTrace()
},
)
}

override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !group.parentOf(Thread.currentThread().threadGroup)
}

override fun dispatch(context: CoroutineContext, block: Runnable) {
if (isDispatchNeeded(context)) {
executor.execute(block)
} else {
block.run()
}
}

operator fun invoke(block: suspend CoroutineScope.() -> Unit) = scope.launch(block = block)
operator fun invoke() = scope

}

private var scope: CoroutineScope = asyncCtx.scope
set(value) {
field = value
if (value != asyncCtx.scope) {
asyncCtx.executor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS)
}
}

@ApiStatus.Internal
fun getStellarCommand(name: String): AbstractStellarCommand<*>? =
commands.firstOrNull { it.name == name.substringAfter(':') || name in it.aliases }
Expand All @@ -32,6 +107,14 @@ object StellarConfig {
this.miniMessage = miniMessage
}

@JvmStatic
fun setScope(scope: CoroutineScope) = apply {
this.scope = scope
}

@JvmStatic
fun getScope() = scope
Comment thread
MrLarkyy marked this conversation as resolved.
Outdated

/**
* Sets the default prefix in [StellarConfig], which will be used a default value.
* @return The modified [StellarConfig].
Expand All @@ -50,4 +133,5 @@ object StellarConfig {
this.plugin = plugin
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.reflect.safeCast

@Suppress("UNCHECKED_CAST")
@ApiStatus.Internal
data class ExecutableExecution<C : CommandSender>(val clazz: KClass<C>, val execution: StellarExecution<C>, val async: Boolean) {
data class ExecutableExecution<C : CommandSender>(val clazz: KClass<C>, val execution: StellarExecution<C>) {
operator fun invoke(context: CommandContext<CommandSender>) {
if (clazz.safeCast(context.sender) == null) return
execution(context as? CommandContext<C> ?: return)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
package com.undefined.stellar.data.execution

import com.undefined.stellar.BukkitCtx
import com.undefined.stellar.data.argument.CommandContext
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import org.bukkit.command.CommandSender
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.CompletableFuture
import kotlin.reflect.KClass
import kotlin.reflect.safeCast

@Suppress("UNCHECKED_CAST")
@ApiStatus.Internal
data class ExecutableRunnable<C : CommandSender>(val alwaysApplicable: Boolean, val clazz: KClass<C>, val runnable: StellarRunnable<C>, val async: Boolean) {
operator fun invoke(context: CommandContext<CommandSender>): Boolean {
if (clazz.safeCast(context.sender) == null) return false
return runnable(context as? CommandContext<C> ?: return false)
data class ExecutableRunnable<C : CommandSender>(
val alwaysApplicable: Boolean,
val clazz: KClass<C>,
val runnable: StellarRunnable<C>
) {
operator fun invoke(context: CommandContext<CommandSender>): CompletableFuture<Boolean> {
if (clazz.safeCast(context.sender) == null) return CompletableFuture.completedFuture(false)
return runnable(context as? CommandContext<C> ?: return CompletableFuture.completedFuture(false))
}

constructor(
alwaysApplicable: Boolean,
clazz: KClass<C>,
runnable: CommandContext<C>.() -> Boolean,
) : this(alwaysApplicable, clazz, StellarRunnable {
BukkitCtx.scope.async {
runnable(it)
}.asCompletableFuture()
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package com.undefined.stellar.data.execution

import com.undefined.stellar.data.argument.CommandContext
import org.bukkit.command.CommandSender
import java.util.concurrent.CompletableFuture

/**
* Represents a functional interface used for command executions.
*/
fun interface StellarRunnable<C : CommandSender> {
operator fun invoke(context: CommandContext<C>): Boolean
operator fun invoke(context: CommandContext<C>): CompletableFuture<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.undefined.stellar.nms

import com.mojang.brigadier.Command
import com.mojang.brigadier.context.CommandContext
import com.undefined.stellar.AbstractStellarCommand
import com.undefined.stellar.StellarConfig
import com.undefined.stellar.data.argument.ArgumentHelper
import org.bukkit.command.CommandSender
import java.util.concurrent.CompletableFuture

abstract class AbstractNMSManager {

protected fun handleExecutions(
command: AbstractStellarCommand<*>,
stellarContext: com.undefined.stellar.data.argument.CommandContext<CommandSender>,
context: CommandContext<Any>,
subArguments: List<AbstractStellarCommand<*>>
): Int {
if (subArguments.any { arg -> arg.runnables.any { it.alwaysApplicable } }) {
for (argument in subArguments) for (runnable in argument.runnables) runnable(stellarContext)
return Command.SINGLE_SUCCESS
}

val rootNodeName = context.rootNode.name.takeIf { it.isNotBlank() }
val baseCommand = StellarConfig.getStellarCommand(context.input.split(' ').first())
?: throw IllegalStateException("Cannot get root command.")

val arguments = ArgumentHelper.getArguments(baseCommand, context, if (rootNodeName != null) 0 else 1)

var cmdFuture: CompletableFuture<Boolean>? = null
for (runnable in baseCommand.runnables) {
cmdFuture = if (cmdFuture == null) runnable(stellarContext)
else cmdFuture.thenCompose { res ->
if (!res) return@thenCompose CompletableFuture.completedFuture(false)
runnable(stellarContext)
}
}
if (cmdFuture == null) cmdFuture = CompletableFuture.completedFuture(true)

cmdFuture!!.thenCompose { res ->
if (!res) return@thenCompose CompletableFuture.completedFuture(res)
val actualArguments = arguments.filter { it.runnables.isNotEmpty() && it != command } + command

var future: CompletableFuture<Boolean>? = null
for (argument in actualArguments) {
for (runnable in argument.runnables) {
future = if (future == null) runnable(stellarContext)
else future.thenCompose { res ->
if (!res) return@thenCompose CompletableFuture.completedFuture(res)
runnable(stellarContext)
}
}
}
future ?: CompletableFuture.completedFuture(true)
}.thenAccept { res ->
if (!res) return@thenAccept
for (execution in command.executions) {
execution(stellarContext)
}
}

/*
for (runnable in baseCommand.runnables.filter { it.async }) if (!runnable(stellarContext)) return@executes 1
for (argument in arguments.filter { it != command } + command) for (runnable in argument.runnables.filter { it.async }) if (!runnable(
stellarContext
)
) return@executes 1
for (execution in command.executions.filter { it.async }) execution(stellarContext)

Bukkit.getScheduler().runTask(plugin, Runnable {
for (runnable in baseCommand.runnables.filter { !it.async }) if (!runnable(stellarContext)) return@Runnable
for (runnable in command.runnables.filter { !it.async }) if (!runnable(stellarContext)) return@Runnable
for (argument in arguments) for (runnable in argument.runnables.filter { !it.async }) if (!runnable(
stellarContext
)
) return@Runnable
for (execution in command.executions.filter { !it.async }) execution(stellarContext)
})
*/
Comment thread
MrLarkyy marked this conversation as resolved.
Outdated
return 1
}

}
11 changes: 7 additions & 4 deletions kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ val packageKotlinSources by tasks.registering(Jar::class) {
from(subprojects.map { it.sourceSets.main.get().allSource })
}

val maven_username = if (env.isPresent("MAVEN_USERNAME")) env.fetch("MAVEN_USERNAME") else ""
val maven_password = if (env.isPresent("MAVEN_PASSWORD")) env.fetch("MAVEN_PASSWORD") else ""

publishing {
publications {
create<MavenPublication>("kotlin") {
Expand Down Expand Up @@ -87,16 +90,16 @@ publishing {
name = "undefined-releases"
url = uri("https://repo.undefinedcreations.com/releases")
credentials(PasswordCredentials::class) {
username = System.getenv("MAVEN_NAME") ?: property("mavenUser").toString()
password = System.getenv("MAVEN_SECRET") ?: property("mavenPassword").toString()
username = maven_username
password = maven_password
}
}
maven {
name = "undefined-snapshots"
url = uri("https://repo.undefinedcreations.com/snapshots")
credentials(PasswordCredentials::class) {
username = System.getenv("MAVEN_NAME") ?: property("mavenUser").toString()
password = System.getenv("MAVEN_SECRET") ?: property("mavenPassword").toString()
username = maven_username
password = maven_password
}
}
}
Expand Down
Loading