Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion .github/workflows/call-gradle-wrapper-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ jobs:
steps:
- uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v3
uses: gradle/actions/wrapper-validation@v6
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
plugins {
alias(libs.plugins.gradle.neoforgegradle) apply false
alias(libs.plugins.klibs.gradle.detekt) apply false
alias(libs.plugins.klibs.gradle.dokka.module)
alias(libs.plugins.klibs.gradle.dokka.root)
alias(libs.plugins.klibs.gradle.java.version) apply false
alias(libs.plugins.klibs.gradle.publication) apply false
alias(libs.plugins.klibs.gradle.rootinfo) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.gradle.forgegradle) apply false
}
52 changes: 52 additions & 0 deletions core-forge/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
plugins {
id("net.minecraftforge.gradle")
id("org.jetbrains.kotlin.jvm")
id("org.jetbrains.kotlin.plugin.serialization")
id("ru.astrainteractive.gradleplugin.detekt")
id("ru.astrainteractive.gradleplugin.java.version")
id("ru.astrainteractive.gradleplugin.publication")
id("ru.astrainteractive.gradleplugin.rootinfo")
}

repositories {
minecraft.mavenizer(this)
mavenCentral()
mavenLocal()
maven(fg.forgeMaven)
maven(fg.minecraftLibsMaven)
}

dependencies {
compileOnly(libs.klibs.mikro.core)
compileOnly(libs.kotlin.coroutines.core)
compileOnly(libs.minecraft.kyori.api)
compileOnly(libs.minecraft.kyori.gson)
compileOnly(libs.minecraft.kyori.legacy)
compileOnly(libs.minecraft.kyori.minimessage)
compileOnly(libs.minecraft.kyori.plain)
compileOnly(libs.minecraft.luckperms)

implementation(projects.command)
implementation(projects.core)

testImplementation(libs.kotlin.serialization.kaml)
testImplementation(libs.tests.kotlin.test)
}

dependencies {
minecraft {
implementation(
dependency(
"net.minecraftforge:forge:" +
libs.versions.minecraft.minecraftforge.minecraft.get() +
"-" +
libs.versions.minecraft.minecraftforge.forge.get()
)
)
mappings("official", libs.versions.minecraft.minecraftforge.minecraft.get())
}
}

configurations.runtimeElements {
setExtendsFrom(emptySet())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
@file:Suppress("TooManyFunctions")

package ru.astrainteractive.astralibs.command.brigadier.command

import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.ArgumentType
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.server.MinecraftServer
import net.minecraft.server.dedicated.DedicatedServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.rcon.RconConsoleSource
import ru.astrainteractive.astralibs.command.api.argumenttype.ArgumentConverter
import ru.astrainteractive.astralibs.command.api.exception.ArgumentConverterException
import ru.astrainteractive.astralibs.command.api.exception.CommandException
import ru.astrainteractive.astralibs.command.api.exception.NoPermissionException
import ru.astrainteractive.astralibs.command.api.exception.NotPlayerExecutorException
import ru.astrainteractive.astralibs.server.permission.Permission
import ru.astrainteractive.astralibs.server.util.asPermissible

fun command(
alias: String,
block: LiteralArgumentBuilder<CommandSourceStack>.() -> Unit
): LiteralArgumentBuilder<CommandSourceStack> {
val literal = Commands.literal(alias)
literal.block()
return literal
}

fun LiteralArgumentBuilder<CommandSourceStack>.literal(
alias: String,
block: LiteralArgumentBuilder<CommandSourceStack>.() -> Unit
) {
val literal = Commands.literal(alias)
literal.block()
this.then(literal)
}

data class BrigadierArgument<T : Any>(
val alias: String,
val type: ArgumentType<T>,
val clazz: Class<T>
)

inline fun <reified T : Any> LiteralArgumentBuilder<CommandSourceStack>.argument(
alias: String,
type: ArgumentType<T>,
noinline block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) = argument(
alias = alias,
type = type,
clazz = T::class.java,
block = block
)

fun <T : Any> LiteralArgumentBuilder<CommandSourceStack>.argument(
alias: String,
type: ArgumentType<T>,
clazz: Class<T>,
block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) {
val argument = Commands.argument(alias, type)
val brigadierArgument = BrigadierArgument(
alias = alias,
type = type,
clazz = clazz
)
argument.block(brigadierArgument)
this.then(argument)
}

inline fun <reified T : Any> RequiredArgumentBuilder<CommandSourceStack, *>.argument(
alias: String,
type: ArgumentType<T>,
noinline block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) = argument(
alias = alias,
type = type,
clazz = T::class.java,
block = block
)

fun <T : Any> RequiredArgumentBuilder<CommandSourceStack, *>.argument(
alias: String,
type: ArgumentType<T>,
clazz: Class<T>,
block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) {
val argument = Commands.argument(alias, type)
val brigadierArgument = BrigadierArgument(
alias = alias,
type = type,
clazz = clazz
)
argument.block(brigadierArgument)
this.then(argument)
}

fun RequiredArgumentBuilder<CommandSourceStack, *>.runs(
onFailure: (CommandContext<CommandSourceStack>, Throwable) -> Unit = { _, _ -> },
block: (RequiredArgumentBuilder<CommandSourceStack, *>.(CommandContext<CommandSourceStack>) -> Unit)
) {
executes { ctx ->
runCatching { block.invoke(this, ctx) }
.onFailure { onFailure.invoke(ctx, it) }
Command.SINGLE_SUCCESS
}
}

fun LiteralArgumentBuilder<CommandSourceStack>.runs(
onFailure: (CommandContext<CommandSourceStack>, Throwable) -> Unit = { _, _ -> },
block: LiteralArgumentBuilder<CommandSourceStack>.(CommandContext<CommandSourceStack>) -> Unit
) {
executes { ctx ->
runCatching { block.invoke(this, ctx) }
.onFailure { onFailure.invoke(ctx, it) }
Command.SINGLE_SUCCESS
}
}

fun RequiredArgumentBuilder<CommandSourceStack, *>.hints(block: (CommandContext<CommandSourceStack>) -> List<String>) {
suggests { context, builder ->
block.invoke(context).forEach(builder::suggest)
builder.buildFuture()
}
}

@Throws(NoPermissionException::class)
fun CommandContext<CommandSourceStack>.requirePermission(permission: Permission) {
if (source.source is RconConsoleSource) return
if (source.source is DedicatedServer) return
if (source.source is MinecraftServer) return
val serverPlayer = source.entity as? ServerPlayer ?: run {
throw CommandException("$source.source; is not a player!")
}
val hasPermission = serverPlayer.asPermissible().hasPermission(permission)
if (!hasPermission) throw NoPermissionException(permission)
}

@Throws(NotPlayerExecutorException::class)
fun CommandContext<CommandSourceStack>.requirePlayer(): ServerPlayer {
return this.source.player ?: throw NotPlayerExecutorException()
}

@Throws(IllegalArgumentException::class)
fun <T : Any> CommandContext<CommandSourceStack>.requireArgument(bArgument: BrigadierArgument<T>): T {
return getArgument(bArgument.alias, bArgument.clazz)
}

@Throws(ArgumentConverterException::class)
fun <T : Any> CommandContext<CommandSourceStack>.requireArgument(
bArgument: BrigadierArgument<String>,
converter: ArgumentConverter<T>
): T {
val string = getArgument(bArgument.alias, bArgument.clazz)
return converter.transform(string)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package ru.astrainteractive.astralibs.command.brigadier.command

import com.mojang.brigadier.arguments.ArgumentType
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.server.MinecraftServer
import net.minecraft.server.dedicated.DedicatedServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.rcon.RconConsoleSource
import ru.astrainteractive.astralibs.command.api.brigadier.command.MultiplatformCommands
import ru.astrainteractive.astralibs.command.api.brigadier.sender.KCommandSender
import ru.astrainteractive.astralibs.command.api.brigadier.sender.KPlayerKCommandSender
import ru.astrainteractive.astralibs.command.brigadier.sender.ForgeRconKCommandSender
import ru.astrainteractive.astralibs.command.brigadier.sender.ForgeServerKCommandSender
import ru.astrainteractive.astralibs.server.util.asOnlineMinecraftPlayer

@Suppress("UNCHECKED_CAST")
class ForgeMultiplatformCommands : MultiplatformCommands {
override fun literal(literal: String): LiteralArgumentBuilder<Any> {
return Commands.literal(literal) as LiteralArgumentBuilder<Any>
}

override fun <T : Any> argument(
name: String,
argumentType: ArgumentType<T>
): RequiredArgumentBuilder<Any, T> {
return Commands.argument(name, argumentType) as RequiredArgumentBuilder<Any, T>
}

override fun getSender(context: CommandContext<*>): KCommandSender {
val commandSourceStack = context.source as CommandSourceStack

return when (val source = commandSourceStack.source) {
is ServerPlayer -> {
KPlayerKCommandSender(source.asOnlineMinecraftPlayer())
}

is RconConsoleSource -> {
ForgeRconKCommandSender(source)
}

is DedicatedServer,
is MinecraftServer -> {
ForgeServerKCommandSender(source)
}

else -> error("Could not wrap sender ${commandSourceStack.source.javaClass}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.astrainteractive.astralibs.command.brigadier.sender

import net.minecraft.server.rcon.RconConsoleSource
import ru.astrainteractive.astralibs.command.api.brigadier.sender.ConsoleKCommandSender
import ru.astrainteractive.astralibs.server.KAudience
import ru.astrainteractive.astralibs.server.KCommandDispatcher
import ru.astrainteractive.astralibs.server.permission.ConsoleKPermissible
import ru.astrainteractive.astralibs.server.permission.KPermissible
import ru.astrainteractive.astralibs.server.util.asKAudience
import ru.astrainteractive.astralibs.server.util.asKCommandDispatcher

class ForgeRconKCommandSender(
sender: RconConsoleSource
) : ConsoleKCommandSender,
KPermissible by ConsoleKPermissible,
KAudience by sender.asKAudience(),
KCommandDispatcher by sender.asKCommandDispatcher()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.astrainteractive.astralibs.command.brigadier.sender

import net.minecraft.server.MinecraftServer
import ru.astrainteractive.astralibs.command.api.brigadier.sender.ConsoleKCommandSender
import ru.astrainteractive.astralibs.server.KAudience
import ru.astrainteractive.astralibs.server.KCommandDispatcher
import ru.astrainteractive.astralibs.server.permission.ConsoleKPermissible
import ru.astrainteractive.astralibs.server.permission.KPermissible
import ru.astrainteractive.astralibs.server.util.asKAudience
import ru.astrainteractive.astralibs.server.util.asKCommandDispatcher

class ForgeServerKCommandSender(
sender: MinecraftServer
) : ConsoleKCommandSender,
KPermissible by ConsoleKPermissible,
KAudience by sender.asKAudience(),
KCommandDispatcher by sender.asKCommandDispatcher()
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ru.astrainteractive.astralibs.command.registrar

import com.mojang.brigadier.builder.LiteralArgumentBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import net.minecraft.commands.CommandSourceStack
import net.minecraftforge.event.RegisterCommandsEvent
import net.minecraftforge.eventbus.api.EventPriority
import ru.astrainteractive.astralibs.command.api.registrar.CommandRegistrarContext
import ru.astrainteractive.astralibs.event.flowEvent

class ForgeCommandRegistrarContext(
private val mainScope: CoroutineScope
) : CommandRegistrarContext {
private val registerCommandsEvent = flowEvent<RegisterCommandsEvent>(EventPriority.HIGHEST)
.filterNotNull()
.stateIn(mainScope, SharingStarted.Eagerly, null)

override fun registerWhenReady(node: LiteralArgumentBuilder<*>) {
node as LiteralArgumentBuilder<CommandSourceStack>
registerCommandsEvent
.mapNotNull { registerCommandsEvent -> registerCommandsEvent?.dispatcher }
.onEach { commandDispatcher -> commandDispatcher.register(node) }
.launchIn(mainScope)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.astrainteractive.astralibs.coroutines

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainCoroutineDispatcher
import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers

class ForgeDispatchers : KotlinDispatchers {
override val Main: MainCoroutineDispatcher by lazy {
ForgeMainDispatcher()
}
override val IO: CoroutineDispatcher
get() = Dispatchers.IO
override val Default: CoroutineDispatcher
get() = Dispatchers.Default
override val Unconfined: CoroutineDispatcher
get() = Dispatchers.Unconfined
}
Loading
Loading