From f623515b94ef3772a1e2cb99ef3411f2e51618dd Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sat, 25 Oct 2025 23:29:35 +0200 Subject: [PATCH] Add Dev Command --- src/main/kotlin/Main.kt | 4 +- src/main/kotlin/commands/dev/DevCommand.kt | 183 ++++++++++++++++++ src/main/kotlin/commands/user/UserCommand.kt | 2 + .../kotlin/commands/user/UserInfoCommand.kt | 2 + .../kotlin/commands/user/UserSearchCommand.kt | 2 + 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/commands/dev/DevCommand.kt diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 8ceb1ea..c820111 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -6,6 +6,7 @@ import de.steamwar.commands.SteamWar import de.steamwar.commands.database.DatabaseCommand import de.steamwar.commands.database.InfoCommand import de.steamwar.commands.database.ResetCommand +import de.steamwar.commands.dev.DevCommand import de.steamwar.commands.user.UserCommand import de.steamwar.commands.user.UserInfoCommand import de.steamwar.commands.user.UserSearchCommand @@ -13,6 +14,7 @@ import de.steamwar.commands.user.UserSearchCommand fun main(args: Array) = SteamWar() .subcommands( DatabaseCommand().subcommands(InfoCommand(), ResetCommand()), - UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()) + UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()), + DevCommand() ) .main(args) \ No newline at end of file diff --git a/src/main/kotlin/commands/dev/DevCommand.kt b/src/main/kotlin/commands/dev/DevCommand.kt new file mode 100644 index 0000000..8e80442 --- /dev/null +++ b/src/main/kotlin/commands/dev/DevCommand.kt @@ -0,0 +1,183 @@ +package de.steamwar.commands.dev + +import com.github.ajalt.clikt.command.SuspendingCliktCommand +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.CliktError +import com.github.ajalt.clikt.core.Context +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.arguments.help +import com.github.ajalt.clikt.parameters.arguments.multiple +import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.help +import com.github.ajalt.clikt.parameters.options.multiple +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.types.file +import com.github.ajalt.clikt.parameters.types.long +import com.sun.security.auth.module.UnixSystem +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import java.io.File + +const val LOG4J_CONFIG = """ + + + + + + + + + + + + + + + + + + + + + + + + +""" + +class DevCommand: CliktCommand("dev") { + override fun help(context: Context): String = "Start a dev Server" + + override val treatUnknownOptionsAsArgs = true + + val server by argument().help("Server Template") + val port by option("--port").long().default(if (System.getProperty("os.name").lowercase().let { os -> listOf("mac", "nix", "sunos").any { it in os } }) UnixSystem().uid else 2050).help("Port for Server") + val world by option("--world", "-w").help("User World") + val plugins by option("--plugins", "-p").help("Plugin Dir") + val profile by option().flag().help("Add Profiling Arguments") + val forceUpgrade by option().flag().help("Force Upgrade") + val jar by option().file().help("Jar File") + val jvmArgs by argument().multiple() + + override val printHelpOnEmptyArgs = true + + val workingDir = File(".").absoluteFile + val log4jConfig = File(workingDir, "log4j2.xml") + + override fun run() { + val args = mutableListOf() + + val serverDirectory = File(workingDir, server) + val serverDir = if (serverDirectory.exists() && serverDirectory.isDirectory) serverDirectory else File(workingDir, server) + + if (isVelocity(server)) { + runServer(args, jvmArgs, listOf(jar?.absolutePath ?: File("/jar/Velocity.jar").absolutePath), serverDir) + } else { + setLogConfig(args) + val version = findVersion(server) ?: throw CliktError("Unknown Server Version") + val worldFile = world?.let { File(workingDir, it) } ?: File(serverDir, "devtempworld") + val jarFile = jar?.absolutePath ?: JARS[server]?.let { VERSIONS[it] } ?: VERSIONS[version] ?: throw CliktError("Unknown Server Version") + + if (!worldFile.exists()) { + val templateFile = File(serverDir, "Bauwelt") + if (!templateFile.exists()) { + throw CliktError("World Template not found!") + } + templateFile.copyRecursively(worldFile) + } + + val devFile = File("/configs/DevServer/${System.getProperty("user.name")}.$port.$version") + if (System.getProperty("user.name") != "minecraft") { + devFile.createNewFile() + } + + runServer(args, jvmArgs, listOf( + jarFile, + if (forceUpgrade) "-forceUpgrade" else "", + "--port", port.toString(), + "--level-name", worldFile.name, + "--world-dir", workingDir.absolutePath, + "--no-gui", + *(if (plugins != null) arrayOf("--plugins", plugins!!) else arrayOf()) + ), serverDir) + + try { + devFile.delete() + } catch (_: Exception) { } + } + } + + val OPENJ9_ARGS = arrayOf("-Xmx1G", + "-Xgc:excessiveGCratio=80", + "-Xsyslog:none", + "-Xtrace:none", + "-Xnoclassgc", + "-Xdisableexplicitgc", + "-XX:+AlwaysPreTouch", + "-XX:+CompactStrings", + "-XX:-HeapDumpOnOutOfMemory", + "-XX:+ExitOnOutOfMemoryError" + ) + + val JAVA17_ARGS = arrayOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED") + + val VERSIONS = mapOf( + 8 to "/jars/paper-1.8.8.jar", + 9 to "/jars/spigot-1.9.4.jar", + 10 to "/jars/paper-1.10.2.jar", + 12 to "/jars/spigot-1.12.2.jar", + 14 to "/jars/spigot-1.14.4.jar", + 15 to "/jars/spigot-1.15.2.jar", + 18 to "/jars/paper-1.18.2.jar", + 19 to "/jars/paper-1.19.3.jar", + 20 to "/jars/paper-1.20.1.jar", + 21 to "/jars/paper-1.21.6.jar" + ) + + val JARS = mapOf( + "Tutorial" to 15, + "Lobby" to 20 + ) + + fun findVersion(server: String): Int? = server.dropWhile { !it.isDigit() }.toIntOrNull() + + fun isJava8(server: String): Boolean = findVersion(server)?.let { it <= 10 } ?: false + + fun isVelocity(server: String): Boolean = server.endsWith("Velocity") + + fun setLogConfig(args: MutableList) { + args += "-DlogPath=${workingDir.absolutePath}/logs" + args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}" + + if (!log4jConfig.exists()) { + log4jConfig.writeText(LOG4J_CONFIG) + } + } + + fun runServer(args: List, jvmArgs: List, cmd: List, serverDir: File) = runBlocking { + val process = ProcessBuilder( + if (isJava8(server)) "/usr/lib/jvm/openj9-8/bin/java" else "java", + *jvmArgs.toTypedArray(), + *args.toTypedArray(), + *OPENJ9_ARGS, + *(if (isJava8(server)) arrayOf() else JAVA17_ARGS), + if (profile) "-javaagent:/jars/LixfelsProfiler.jar=start" else "", + "-Xshareclasses:nonfatal,name=$server", + "-jar", + *cmd.toTypedArray() + ).directory(serverDir).start() + + launch { + process.inputStream.transferTo(System.out) + } + launch { + process.errorStream.transferTo(System.err) + } + launch { + System.`in`.transferTo(process.outputStream) + } + process.waitFor() + } +} \ No newline at end of file diff --git a/src/main/kotlin/commands/user/UserCommand.kt b/src/main/kotlin/commands/user/UserCommand.kt index ba363b4..370cbe0 100644 --- a/src/main/kotlin/commands/user/UserCommand.kt +++ b/src/main/kotlin/commands/user/UserCommand.kt @@ -1,7 +1,9 @@ package de.steamwar.commands.user import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.Context class UserCommand: CliktCommand("user") { override fun run() = Unit + override fun help(context: Context): String = "User related commands" } \ No newline at end of file diff --git a/src/main/kotlin/commands/user/UserInfoCommand.kt b/src/main/kotlin/commands/user/UserInfoCommand.kt index b2feab7..607f284 100644 --- a/src/main/kotlin/commands/user/UserInfoCommand.kt +++ b/src/main/kotlin/commands/user/UserInfoCommand.kt @@ -21,6 +21,8 @@ class UserInfoCommand : CliktCommand("info") { val userId by argument().help("Id, Name, UUID or DiscordId") val user by lazy { findUser(userId) ?: throw CliktError("User not found") } + override val printHelpOnEmptyArgs = true + @OptIn(ExperimentalTime::class) override fun run() { transaction { diff --git a/src/main/kotlin/commands/user/UserSearchCommand.kt b/src/main/kotlin/commands/user/UserSearchCommand.kt index eea354a..ec26f55 100644 --- a/src/main/kotlin/commands/user/UserSearchCommand.kt +++ b/src/main/kotlin/commands/user/UserSearchCommand.kt @@ -16,6 +16,8 @@ import org.jetbrains.exposed.v1.jdbc.transactions.transaction class UserSearchCommand : CliktCommand("search") { val query by argument().help("Name, Id, UUID or DiscordId") + override val printHelpOnEmptyArgs = true + override fun help(context: Context): String = "Search for users" override fun run() = transaction {