diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/App.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/App.kt index 092b7dd..8025c1f 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/App.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/App.kt @@ -1,25 +1,26 @@ package xyz.brysonsteck.ServerCraft -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; +import javafx.application.Application +import javafx.fxml.FXMLLoader +import javafx.scene.Parent +import javafx.scene.Scene import javafx.scene.image.Image -import javafx.stage.Stage; -import java.awt.Desktop; +import javafx.stage.Stage class App : Application() { - override fun start(stage: Stage) { + override fun start(stage: Stage) { var scene = Scene(loadFXML("primary"), 963.0, 713.0) + // Application.setUserAgentStylesheet(CupertinoLight().getUserAgentStylesheet()) + scene.stylesheets.add(this.javaClass.getResource("css/style.css").toExternalForm()) stage.icons.add(Image(this.javaClass.getResourceAsStream("app.png"))) - stage.setResizable(false) + stage.setResizable(true) stage.title = "ServerCraft" stage.scene = scene stage.show() } - public fun loadFXML(fxml: String) : Parent { + public fun loadFXML(fxml: String): Parent { val fxmlLoader = FXMLLoader(this.javaClass.getResource(fxml + ".fxml")) return fxmlLoader.load() } @@ -27,5 +28,4 @@ class App : Application() { public fun run() { launch() } - } diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/Main.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/Main.kt index fbb61e4..5e445fe 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/Main.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/Main.kt @@ -2,4 +2,4 @@ package xyz.brysonsteck.ServerCraft fun main() { App().run() -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/InfoController.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/InfoController.kt index 92c6674..206f2ff 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/InfoController.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/InfoController.kt @@ -1,27 +1,21 @@ package xyz.brysonsteck.ServerCraft.controllers +import java.awt.Desktop +import java.net.URI +import java.util.Properties +import javafx.event.ActionEvent import javafx.fxml.FXML -import javafx.application.Platform import javafx.scene.Node import javafx.scene.control.Hyperlink import javafx.scene.control.Label import javafx.stage.Stage -import javafx.event.ActionEvent -import java.awt.Desktop -import java.net.URI -import java.util.Properties import xyz.brysonsteck.ServerCraft.App class InfoController { - @FXML - lateinit private var version: Label + @FXML lateinit private var version: Label - private val emails = mapOf( - "bryson" to "me@brysonsteck.xyz" - ) - private val websites = mapOf( - "bryson" to "https://brysonsteck.xyz" - ) + private val emails = mapOf("bryson" to "me@brysonsteck.xyz") + private val websites = mapOf("bryson" to "https://brysonsteck.xyz") private val source = "https://codeberg.org/brysonsteck/ServerCraft" private val license = "https://www.gnu.org/licenses/gpl-3.0.html" @@ -79,8 +73,8 @@ class InfoController { @FXML private fun closeInfo(e: ActionEvent) { - val source = e.getSource() as Node + val source = e.getSource() as Node val stage = source.getScene().getWindow() as Stage - stage.close(); + stage.close() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt index 1d8af46..1bce098 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt @@ -1,136 +1,76 @@ package xyz.brysonsteck.ServerCraft.controllers -import kotlinx.coroutines.* -import kotlinx.coroutines.javafx.JavaFx -import org.rauschig.jarchivelib.* - import java.io.File import java.io.IOException -import java.io.BufferedReader import java.io.InputStreamReader -import java.awt.Checkbox -import java.awt.Desktop -import java.util.Properties import java.net.URL -import java.net.URI - -import javafx.beans.value.ChangeListener -import javafx.beans.value.ObservableValue -import javafx.concurrent.Task -import javafx.beans.property.BooleanProperty import javafx.collections.FXCollections +import javafx.event.ActionEvent import javafx.fxml.FXML import javafx.fxml.FXMLLoader -import javafx.geometry.Insets +import javafx.scene.Scene import javafx.scene.control.Button -import javafx.scene.control.ChoiceBox -import javafx.scene.control.Label -import javafx.scene.control.TextField -import javafx.scene.control.Spinner -import javafx.scene.control.TitledPane import javafx.scene.control.ButtonBar import javafx.scene.control.CheckBox +import javafx.scene.control.ChoiceBox +import javafx.scene.control.Label import javafx.scene.control.ProgressBar -import javafx.scene.control.Hyperlink import javafx.scene.control.ScrollPane -import javafx.scene.layout.Border -import javafx.scene.layout.BorderStroke -import javafx.scene.layout.GridPane -import javafx.scene.layout.Pane -import javafx.scene.layout.HBox -import javafx.scene.layout.VBox -import javafx.scene.text.TextAlignment -import javafx.scene.text.Text -import javafx.scene.Scene -import javafx.scene.input.MouseEvent +import javafx.scene.control.Spinner +import javafx.scene.control.TextField import javafx.scene.image.Image import javafx.scene.image.ImageView -import javafx.stage.FileChooser -import javafx.stage.FileChooser.ExtensionFilter +import javafx.scene.layout.HBox +import javafx.scene.layout.Pane import javafx.stage.DirectoryChooser import javafx.stage.Modality import javafx.stage.Stage -import javafx.event.EventHandler -import javafx.event.ActionEvent +import kotlinx.coroutines.* +import kotlinx.coroutines.javafx.JavaFx import org.rauschig.jarchivelib.* - -import xyz.brysonsteck.ServerCraft.server.Server -import xyz.brysonsteck.ServerCraft.server.Download import xyz.brysonsteck.ServerCraft.App -import xyz.brysonsteck.ServerCraft.dialogs.Dialog import xyz.brysonsteck.ServerCraft.dialogs.CommonDialog -import xyz.brysonsteck.ServerCraft.dialogs.KillDialog +import xyz.brysonsteck.ServerCraft.dialogs.Dialog import xyz.brysonsteck.ServerCraft.dialogs.EulaDialog +import xyz.brysonsteck.ServerCraft.dialogs.KillDialog +import xyz.brysonsteck.ServerCraft.server.Download +import xyz.brysonsteck.ServerCraft.server.Server class PrimaryController { - @FXML - lateinit private var primary: Pane - @FXML - lateinit private var currentDirectoryLabel: Label - @FXML - lateinit private var worldNameField: TextField - @FXML - lateinit private var seedField: TextField - @FXML - lateinit private var portSpinner: Spinner - @FXML - lateinit private var difficultyBox: ChoiceBox - @FXML - lateinit private var gamemodeBox: ChoiceBox - @FXML - lateinit private var worldTypeBox: ChoiceBox - @FXML - lateinit private var worldSettingsPane: HBox - @FXML - lateinit private var parentPane: Pane - @FXML - lateinit private var directoryPane: Pane - @FXML - lateinit private var buttonBar: ButtonBar - @FXML - lateinit private var flightCheckbox: CheckBox - @FXML - lateinit private var netherCheckbox: CheckBox - @FXML - lateinit private var structuresCheckbox: CheckBox - @FXML - lateinit private var pvpCheckbox: CheckBox - @FXML - lateinit private var whitelistCheckbox: CheckBox - @FXML - lateinit private var cmdBlocksCheckbox: CheckBox - @FXML - lateinit private var playerCountCheckbox: CheckBox - @FXML - lateinit private var maxPlayerSpinner: Spinner - @FXML - lateinit private var maxSizeSpinner: Spinner - @FXML - lateinit private var memorySpinner: Spinner - @FXML - lateinit private var spawnSpinner: Spinner - @FXML - lateinit private var simulationSpinner: Spinner - @FXML - lateinit private var renderSpinner: Spinner - @FXML - lateinit private var maxTickSpinner: Spinner - @FXML - lateinit private var statusBar: Label - @FXML - lateinit private var progressBar: ProgressBar - @FXML - lateinit private var startButton: Button - @FXML - lateinit private var buildButton: Button - @FXML - lateinit private var defaultsButton: Button - @FXML - lateinit private var dropDownIcon: ImageView - @FXML - lateinit private var console: Label - @FXML - lateinit private var scrollPane: ScrollPane + @FXML lateinit private var primary: Pane + @FXML lateinit private var currentDirectoryLabel: Label + @FXML lateinit private var worldNameField: TextField + @FXML lateinit private var seedField: TextField + @FXML lateinit private var portSpinner: Spinner + @FXML lateinit private var difficultyBox: ChoiceBox + @FXML lateinit private var gamemodeBox: ChoiceBox + @FXML lateinit private var worldTypeBox: ChoiceBox + @FXML lateinit private var worldSettingsPane: HBox + @FXML lateinit private var parentPane: Pane + @FXML lateinit private var directoryPane: Pane + @FXML lateinit private var buttonBar: ButtonBar + @FXML lateinit private var flightCheckbox: CheckBox + @FXML lateinit private var netherCheckbox: CheckBox + @FXML lateinit private var structuresCheckbox: CheckBox + @FXML lateinit private var pvpCheckbox: CheckBox + @FXML lateinit private var whitelistCheckbox: CheckBox + @FXML lateinit private var cmdBlocksCheckbox: CheckBox + @FXML lateinit private var playerCountCheckbox: CheckBox + @FXML lateinit private var maxPlayerSpinner: Spinner + @FXML lateinit private var maxSizeSpinner: Spinner + @FXML lateinit private var memorySpinner: Spinner + @FXML lateinit private var spawnSpinner: Spinner + @FXML lateinit private var simulationSpinner: Spinner + @FXML lateinit private var renderSpinner: Spinner + @FXML lateinit private var maxTickSpinner: Spinner + @FXML lateinit private var statusBar: Label + @FXML lateinit private var progressBar: ProgressBar + @FXML lateinit private var startButton: Button + @FXML lateinit private var buildButton: Button + @FXML lateinit private var defaultsButton: Button + @FXML lateinit private var dropDownIcon: ImageView + @FXML lateinit private var console: Label + @FXML lateinit private var scrollPane: ScrollPane lateinit private var server: Server lateinit private var killDialog: Dialog @@ -150,97 +90,84 @@ class PrimaryController { @FXML public fun initialize() { - scrollPane.vvalueProperty().bind(console.heightProperty()); - difficultyBox.items = FXCollections.observableArrayList( - "Peaceful", - "Easy", - "Normal", - "Hard", - "Hardcore" - ) + // scrollPane.vvalueProperty().bind(console.heightProperty()); + difficultyBox.items = + FXCollections.observableArrayList("Peaceful", "Easy", "Normal", "Hard", "Hardcore") difficultyBox.value = "Normal" - difficultyBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> + difficultyBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> if (!loading) { onPropChange("difficulty", difficultyBox.items[new as Int]) } } - gamemodeBox.items = FXCollections.observableArrayList( - "Survival", - "Creative", - "Adventure", - "Spectator" - ) + gamemodeBox.items = + FXCollections.observableArrayList("Survival", "Creative", "Adventure", "Spectator") gamemodeBox.value = "Survival" - gamemodeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> + gamemodeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> if (!loading) { onPropChange("gamemode", gamemodeBox.items[new as Int]) } } - worldTypeBox.items = FXCollections.observableArrayList( - "Normal", - "Superflat", - "Large Biomes", - "Amplified" - ) + worldTypeBox.items = + FXCollections.observableArrayList("Normal", "Superflat", "Large Biomes", "Amplified") worldTypeBox.value = "Normal" - worldTypeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> + worldTypeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> if (!loading) { onPropChange("level-type", worldTypeBox.items[new as Int]) } } - maxPlayerSpinner.editor.textProperty().addListener { _, _, new -> + maxPlayerSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("max-players", new) } } - maxSizeSpinner.editor.textProperty().addListener { _, _, new -> + maxSizeSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("max-world-size", new) } } - portSpinner.editor.textProperty().addListener { _, _, new -> + portSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("server-port", new) } } - renderSpinner.editor.textProperty().addListener { _, _, new -> + renderSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("view-distance", new) } } - memorySpinner.editor.textProperty().addListener { _, _, new -> + memorySpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("jvm-ram", new) } } - spawnSpinner.editor.textProperty().addListener { _, _, new -> + spawnSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("spawn-protection", new) } } - simulationSpinner.editor.textProperty().addListener { _, _, new -> + simulationSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("simulation-distance", new) } } - maxTickSpinner.editor.textProperty().addListener { _, _, new -> + maxTickSpinner.editor.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("max-tick-time", new) } } - worldNameField.textProperty().addListener { _, _, new -> + worldNameField.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("level-name", new) } } - seedField.textProperty().addListener { _, _, new -> + seedField.textProperty().addListener { _, _, new -> if (!loading) { onPropChange("level-seed", new) } } killDialog = KillDialog() } - + @FXML private fun onDirectoryButtonClick() { val dirChooser = DirectoryChooser() @@ -293,14 +220,17 @@ class PrimaryController { spawnSpinner.valueFactory.value = server.getProp("spawn-protection").toIntOrNull() simulationSpinner.valueFactory.value = server.getProp("simulation-distance").toIntOrNull() maxTickSpinner.valueFactory.value = server.getProp("max-tick-time").toIntOrNull() - difficultyBox.value = if (parseBool(server.getProp("hardcore"))) { - "Hardcore" - } else { - server.getProp("difficulty").replaceFirstChar { it.uppercase() } - } + difficultyBox.value = + if (parseBool(server.getProp("hardcore"))) { + "Hardcore" + } else { + server.getProp("difficulty").replaceFirstChar { it.uppercase() } + } gamemodeBox.value = server.getProp("gamemode").replaceFirstChar { it.uppercase() } - worldTypeBox.value = server.getProp("level-type").removePrefix("minecraft:") - .split('_').joinToString(" ") { it.replaceFirstChar(Char::uppercaseChar)} + worldTypeBox.value = + server.getProp("level-type").removePrefix("minecraft:").split('_').joinToString(" ") { + it.replaceFirstChar(Char::uppercaseChar) + } worldNameField.text = server.getProp("level-name") seedField.text = server.getProp("level-seed") loading = false @@ -373,7 +303,13 @@ class PrimaryController { @FXML private fun onDefaults() { - val res = CommonDialog("info", "Reset all settings?", "Reset settings to defaults?\nThere is NO GOING BACK!").show() + val res = + CommonDialog( + "info", + "Reset all settings?", + "Reset settings to defaults?\nThere is NO GOING BACK!" + ) + .show() if (res) { server.loadProps() applyProps() @@ -387,7 +323,7 @@ class PrimaryController { val scene = Scene(FXMLLoader(App().javaClass.getResource("info.fxml")).load(), 398.0, 358.0) stage.icons.add(Image(App().javaClass.getResourceAsStream("app.png"))) stage.setResizable(false) - stage.initModality(Modality.APPLICATION_MODAL); + stage.initModality(Modality.APPLICATION_MODAL) stage.title = "About ServerCraft" stage.scene = scene stage.show() @@ -399,7 +335,7 @@ class PrimaryController { building = false statusBar.text = "Cancelling..." buildButton.isDisable = true - return; + return } building = true worldSettingsPane.isDisable = true @@ -429,24 +365,28 @@ class PrimaryController { } // download files - val downloads = mapOf( - "Java 20" to "https://download.java.net/java/GA/jdk20.0.1/b4887098932d415489976708ad6d1a4b/9/GPL/${javaFile}", - "BuildTools" to "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar", - ) - val destinations = mapOf ( - "Java 20" to directory + "ServerCraft" + File.separator + "Java" + File.separator, - "BuildTools" to directory + "ServerCraft" + File.separator + "Spigot" + File.separator - ) + val downloads = + mapOf( + "Java 20" to + "https://download.java.net/java/GA/jdk20.0.1/b4887098932d415489976708ad6d1a4b/9/GPL/${javaFile}", + "BuildTools" to + "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar", + ) + val destinations = + mapOf( + "Java 20" to + directory + "ServerCraft" + File.separator + "Java" + File.separator, + "BuildTools" to + directory + "ServerCraft" + File.separator + "Spigot" + File.separator + ) val spigotBuilt = File(destinations["BuildTools"]).exists() - val javaExtracted = File(destinations["Java 20"] + "jdk-20.0.1").exists() - destinations.forEach { - File(it.value).mkdir() - } + val javaExtracted = File(destinations["Java 20"] + "jdk-20.0.1").exists() + destinations.forEach { File(it.value).mkdir() } downloads.forEach { if (it.key == "Java 20" && javaExtracted) { return@forEach } - withContext(Dispatchers.JavaFx){ + withContext(Dispatchers.JavaFx) { statusBar.text = "Downloading ${it.key}..." progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS log("Downloading ${it.key} from ${it.value}\n") @@ -455,15 +395,18 @@ class PrimaryController { download.start() while (download.status == Download.Status.DOWNLOADING) { var prog = (download.downloaded.toDouble() / download.contentLength.toDouble()) - // for whatever reason I need to print something to the screen in order for it to update the progress bar - withContext(Dispatchers.JavaFx) {log("Progress: ${prog * 100}%\n")} + // for whatever reason I need to print something to the screen in order for it to update + // the progress bar + withContext(Dispatchers.JavaFx) { log("Progress: ${prog * 100}%\n") } if (prog >= 0.01) { - withContext(Dispatchers.JavaFx) {progressBar.progress = prog} + withContext(Dispatchers.JavaFx) { progressBar.progress = prog } } if (!building) download.status = Download.Status.CANCELLED Thread.sleep(300) } - withContext(Dispatchers.JavaFx) {log("Download of ${it.key} complete with status: ${download.status}\n")} + withContext(Dispatchers.JavaFx) { + log("Download of ${it.key} complete with status: ${download.status}\n") + } } // extract java archive @@ -473,18 +416,38 @@ class PrimaryController { statusBar.text = "Extracting Java archive..." log("Extracting Java archive to ${directory + "ServerCraft" + File.separator + "Java"}\n") } - var stream = archiver.stream(File(directory + "ServerCraft" + File.separator + "Java" + File.separator + javaFile)) + var stream = + archiver.stream( + File( + directory + + "ServerCraft" + + File.separator + + "Java" + + File.separator + + javaFile + ) + ) val dest = File(directory + "ServerCraft" + File.separator + "Java") var entries = 0.0 - while(stream.getNextEntry() != null && building) { + while (stream.getNextEntry() != null && building) { entries++ } - stream = archiver.stream(File(directory + "ServerCraft" + File.separator + "Java" + File.separator + javaFile)) + stream = + archiver.stream( + File( + directory + + "ServerCraft" + + File.separator + + "Java" + + File.separator + + javaFile + ) + ) var entry = stream.getNextEntry() var currentEntry = 0.0 do { withContext(Dispatchers.JavaFx) { - progressBar.progress = currentEntry/entries + progressBar.progress = currentEntry / entries log(entry.name + "\n") } entry.extract(dest) @@ -498,7 +461,16 @@ class PrimaryController { progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS statusBar.text = "Building Minecraft Server..." } - val builder = ProcessBuilder("${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", "-jar", "BuildTools.jar", "--rev", "latest", "-o", ".." + File.separator + ".." + File.separator) + val builder = + ProcessBuilder( + "${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", + "-jar", + "BuildTools.jar", + "--rev", + "latest", + "-o", + ".." + File.separator + ".." + File.separator + ) builder.directory(File(directory + "ServerCraft" + File.separator + "Spigot")) val proc = builder.start() val reader = InputStreamReader(proc.inputStream) @@ -509,40 +481,48 @@ class PrimaryController { if (!building) { proc.destroy() } - withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))} + withContext(Dispatchers.JavaFx) { log(cbuf.joinToString(separator = "")) } cbuf = CharArray(1000) reader.read(cbuf, 0, 1000) currentline++ if (currentline > 15) { - withContext(Dispatchers.JavaFx) {progressBar.progress = if (spigotBuilt) {currentline/1100.0} else {currentline/14122.0} } + withContext(Dispatchers.JavaFx) { + progressBar.progress = + if (spigotBuilt) { + currentline / 1100.0 + } else { + currentline / 14122.0 + } + } } } cbuf = CharArray(1000) reader.read(cbuf, 0, 1000) - withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))} + withContext(Dispatchers.JavaFx) { log(cbuf.joinToString(separator = "")) } } catch (e: IOException) { - withContext(Dispatchers.JavaFx) {log("Stream Closed\n")} - } + withContext(Dispatchers.JavaFx) { log("Stream Closed\n") } + } } progressBar.isVisible = false findServerJar() - withContext(Dispatchers.JavaFx){ + withContext(Dispatchers.JavaFx) { worldSettingsPane.isDisable = false directoryPane.isDisable = false parentPane.isDisable = false defaultsButton.isDisable = false buildButton.text = "Build Server" buildButton.isDisable = false - statusBar.text = if (building) { - findServerJar() - buildButton.text = "Rebuild Server" - startButton.isDisable = false - "Ready." - } else { - "Server build cancelled." - } - building = false; + statusBar.text = + if (building) { + findServerJar() + buildButton.text = "Rebuild Server" + startButton.isDisable = false + "Ready." + } else { + "Server build cancelled." + } + building = false } } } @@ -551,18 +531,19 @@ class PrimaryController { private fun onStart() { if (started) { killDialog.show() - return; + return } if (!File(directory + "eula.txt").exists()) { val res = EulaDialog().show() if (res) { File(directory + "eula.txt").writeText("eula=true") } else { - return; + return } } started = true - statusBar.text = "The Minecraft Server is now running. Shutdown the server to unlock the settings." + statusBar.text = + "The Minecraft Server is now running. Shutdown the server to unlock the settings." worldSettingsPane.isDisable = true directoryPane.isDisable = true parentPane.isDisable = true @@ -571,7 +552,13 @@ class PrimaryController { startButton.text = "Kill Server" @Suppress("OPT_IN_USAGE") GlobalScope.launch(Dispatchers.Default) { - val builder = ProcessBuilder("${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", "-Xmx${server.getProp("jvm-ram")}M", "-jar", "${server.jar}") + val builder = + ProcessBuilder( + "${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", + "-Xmx${server.getProp("jvm-ram")}M", + "-jar", + "${server.jar}" + ) builder.directory(File(directory)) val proc = builder.start() val reader = InputStreamReader(proc.inputStream) @@ -586,23 +573,24 @@ class PrimaryController { } proc.destroy() } - withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))} + withContext(Dispatchers.JavaFx) { log(cbuf.joinToString(separator = "")) } cbuf = CharArray(1000) reader.read(cbuf, 0, 1000) } cbuf = CharArray(1000) reader.read(cbuf, 0, 1000) - withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))} + withContext(Dispatchers.JavaFx) { log(cbuf.joinToString(separator = "")) } } catch (e: IOException) { - withContext(Dispatchers.JavaFx) {log("Stream Closed\n")} + withContext(Dispatchers.JavaFx) { log("Stream Closed\n") } } withContext(Dispatchers.JavaFx) { - statusBar.text = if (killDialog.result) { - killDialog.result = false - "Server killed." - } else { - "Server stopped." - } + statusBar.text = + if (killDialog.result) { + killDialog.result = false + "Server killed." + } else { + "Server stopped." + } worldSettingsPane.isDisable = false directoryPane.isDisable = false parentPane.isDisable = false @@ -612,19 +600,18 @@ class PrimaryController { startButton.text = "Start Server" started = false } - } + } } private fun loadServerDir(dir: String): Boolean { directory = dir // exit if doesn't exist for whatever reason if (!File(directory).isDirectory) { - return false; + return false } // add system dir separator for cleaner code - if (directory[directory.length-1] != File.separatorChar) - directory += File.separatorChar + if (directory[directory.length - 1] != File.separatorChar) directory += File.separatorChar val hasDummy = File(directory + "ServerCraft").isDirectory val hasProperties = File(directory + File.separator + "server.properties").isFile @@ -641,19 +628,27 @@ class PrimaryController { statusBar.text = "Server needs to be built before starting." } else if (!hasDummy && hasServer) { // server created externally - val result = CommonDialog("warning", directory, "This server directory was not created by \nServerCraft. Errors may occur; copying\nthe world directories to a new folder may be\nsafer. Proceed anyway?").show() + val result = + CommonDialog( + "warning", + directory, + "This server directory was not created by \nServerCraft. Errors may occur; copying\nthe world directories to a new folder may be\nsafer. Proceed anyway?" + ) + .show() statusBar.text = "Ready." if (result) { startButton.isDisable = true File(directory + "ServerCraft").mkdir() buildButton.text = "Build Server" statusBar.text = "Server converted. Build the server to start." - server.loadProps(dir, convert=true) + server.loadProps(dir, convert = true) } return result } else { // assume clean directory - val result = CommonDialog("info", directory, "There is no server in this directory.\nCreate one?").show() + val result = + CommonDialog("info", directory, "There is no server in this directory.\nCreate one?") + .show() if (result) { File(directory + "ServerCraft").mkdir() startButton.isDisable = true @@ -671,7 +666,7 @@ class PrimaryController { server.loadProps() } - return true; + return true } private fun findServerJar(): Boolean { @@ -681,10 +676,7 @@ class PrimaryController { // patch number for (j in 15 downTo 0) { var spigotFile: String = "" - if (j == 0) - spigotFile += "spigot-1.$i.jar" - else - spigotFile += "spigot-1.$i.$j.jar"; + if (j == 0) spigotFile += "spigot-1.$i.jar" else spigotFile += "spigot-1.$i.$j.jar" if (File(directory + spigotFile).isFile) { server.jar = directory + spigotFile @@ -701,4 +693,4 @@ class PrimaryController { return false } -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/CommonDialog.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/CommonDialog.kt index 6a8f14d..df21c06 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/CommonDialog.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/CommonDialog.kt @@ -1,10 +1,10 @@ package xyz.brysonsteck.ServerCraft.dialogs -import javafx.scene.control.Button -import javafx.event.EventHandler import javafx.event.ActionEvent +import javafx.event.EventHandler +import javafx.scene.control.Button -class CommonDialog: Dialog { +class CommonDialog : Dialog { override var type: String override var title: String override var msg: String @@ -18,17 +18,18 @@ class CommonDialog: Dialog { this.msg = msg noButton = Button("No") - noButton.onAction = EventHandler() { - result = false - dialog.hide() - } + noButton.onAction = + EventHandler() { + result = false + dialog.hide() + } yesButton = Button("Yes") - yesButton.onAction = EventHandler() { - result = true - dialog.hide() - } + yesButton.onAction = + EventHandler() { + result = true + dialog.hide() + } yesButton.isDefaultButton = true neutralButton = null } - -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/Dialog.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/Dialog.kt index 1c1c60a..05bea3a 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/Dialog.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/Dialog.kt @@ -1,17 +1,15 @@ package xyz.brysonsteck.ServerCraft.dialogs +import javafx.geometry.Insets +import javafx.scene.Scene import javafx.scene.control.Button import javafx.scene.control.ButtonBar import javafx.scene.control.Label import javafx.scene.image.Image import javafx.scene.image.ImageView import javafx.scene.layout.Pane -import javafx.scene.Scene import javafx.stage.Modality import javafx.stage.Stage -import javafx.geometry.Insets -import javafx.event.EventHandler -import javafx.event.ActionEvent import xyz.brysonsteck.ServerCraft.App abstract class Dialog { @@ -33,7 +31,7 @@ abstract class Dialog { val icon = Image("$resources") dialog.icons.add(Image(App().javaClass.getResourceAsStream("app.png"))) dialog.setResizable(false) - dialog.initModality(Modality.APPLICATION_MODAL); + dialog.initModality(Modality.APPLICATION_MODAL) dialog.title = title val imagePane = Pane() imagePane.layoutX = 14.0 @@ -64,14 +62,13 @@ abstract class Dialog { ButtonBar.setButtonData(neutralButton, ButtonBar.ButtonData.LEFT) buttonBar.buttons.add(neutralButton) } - val dialogScene = Scene(scenePane, 400.0, 150.0); - dialog.setScene(dialogScene); + val dialogScene = Scene(scenePane, 400.0, 150.0) + dialog.setScene(dialogScene) if (hold) { - dialog.showAndWait(); + dialog.showAndWait() } else { - dialog.show(); + dialog.show() } return result } - -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/EulaDialog.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/EulaDialog.kt index 4084b08..835356b 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/EulaDialog.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/EulaDialog.kt @@ -1,13 +1,12 @@ package xyz.brysonsteck.ServerCraft.dialogs -import xyz.brysonsteck.ServerCraft.dialogs.Dialog -import javafx.scene.control.Button -import javafx.event.EventHandler -import javafx.event.ActionEvent import java.awt.Desktop import java.net.URI +import javafx.event.ActionEvent +import javafx.event.EventHandler +import javafx.scene.control.Button -class EulaDialog: Dialog { +class EulaDialog : Dialog { override val type: String override val title: String override val msg: String @@ -18,38 +17,40 @@ class EulaDialog: Dialog { constructor() { type = "warning" title = "Minecraft End User License Agreement (EULA)" - msg = "Do you agree to the terms of the Minecraft End User License Agreement?" + msg = "Do you agree to the terms of the Minecraft\nEnd User License Agreement?" noButton = Button("I Disagree") - noButton.onAction = EventHandler() { - result = false - dialog.hide() - } + noButton.onAction = + EventHandler() { + result = false + dialog.hide() + } noButton.isDefaultButton = true yesButton = Button("I Agree") - yesButton.onAction = EventHandler() { - result = true - dialog.hide() - } + yesButton.onAction = + EventHandler() { + result = true + dialog.hide() + } neutralButton = Button("View EULA") - neutralButton.onAction = EventHandler() { - val desktop = Desktop.getDesktop() - if (desktop.isSupported(Desktop.Action.BROWSE)) { - // most likely running on Windows or macOS - try { - desktop.browse(URI("https://account.mojang.com/documents/minecraft_eula")) - } catch (e: Exception) { - println(e) - } - } else { - // assume running on linux - try { - Runtime.getRuntime().exec("xdg-open https://account.mojang.com/documents/minecraft_eula"); - } catch (e: Exception) { - println(e) - } - } - } + neutralButton.onAction = + EventHandler() { + val desktop = Desktop.getDesktop() + if (desktop.isSupported(Desktop.Action.BROWSE)) { + // most likely running on Windows or macOS + try { + desktop.browse(URI("https://www.minecraft.net/en-us/eula")) + } catch (e: Exception) { + println(e) + } + } else { + // assume running on linux + try { + Runtime.getRuntime().exec("xdg-open https://www.minecraft.net/en-us/eula") + } catch (e: Exception) { + println(e) + } + } + } } - -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/KillDialog.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/KillDialog.kt index 5ef0bbc..1d2d35d 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/KillDialog.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/dialogs/KillDialog.kt @@ -1,10 +1,10 @@ package xyz.brysonsteck.ServerCraft.dialogs -import javafx.scene.control.Button -import javafx.event.EventHandler import javafx.event.ActionEvent +import javafx.event.EventHandler +import javafx.scene.control.Button -class KillDialog: Dialog { +class KillDialog : Dialog { override val type: String override val title: String override val msg: String @@ -15,20 +15,23 @@ class KillDialog: Dialog { constructor() { type = "warning" title = "Server is still running!" - msg = "You should only kill the server if absolutely necessary, i.e. not being responsive after long periods of time. Data loss may occur. Continue anyway?" + msg = + "You should only kill the server if absolutely necessary, i.e. not being responsive after long periods of time. Data loss may occur. Continue anyway?" hold = false noButton = Button("No") - noButton.onAction = EventHandler() { - result = false - dialog.hide() - } + noButton.onAction = + EventHandler() { + result = false + dialog.hide() + } yesButton = Button("Yes") - yesButton.onAction = EventHandler() { - result = true - dialog.hide() - } + yesButton.onAction = + EventHandler() { + result = true + dialog.hide() + } yesButton.isDefaultButton = true neutralButton = null } -} \ No newline at end of file +} diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt index 14606f6..fd38f92 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt @@ -1,13 +1,17 @@ package xyz.brysonsteck.ServerCraft.server -import java.io.*; -import java.net.*; -import java.util.*; +import java.io.* +import java.net.* +import java.util.* import javax.net.ssl.HttpsURLConnection -class Download: Runnable { +class Download : Runnable { public enum class Status { - DOWNLOADING, PAUSED, COMPLETE, CANCELLED, ERROR + DOWNLOADING, + PAUSED, + COMPLETE, + CANCELLED, + ERROR } public var size: Int public var downloaded: Int @@ -19,7 +23,7 @@ class Download: Runnable { private var url: URL private var dir: String - constructor (url: URL, dir: String) { + constructor(url: URL, dir: String) { this.url = url this.dir = dir size = -1 @@ -38,19 +42,19 @@ class Download: Runnable { return filename.substring(filename.lastIndexOf('/') + 1) } - override fun run() { + override fun run() { var stream: InputStream? = null var file: RandomAccessFile? = null try { // Open connection to URL. - var connection = url.openConnection() as HttpsURLConnection; + var connection = url.openConnection() as HttpsURLConnection // Specify what portion of file to download. - connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); + connection.setRequestProperty("Range", "bytes=" + downloaded + "-") // Connect to server. - connection.connect(); + connection.connect() // Make sure response code is in the 200 range. if (connection.responseCode / 100 != 2) { @@ -58,64 +62,63 @@ class Download: Runnable { } // Check for valid content length. - contentLength = connection.getContentLength(); + contentLength = connection.getContentLength() if (contentLength < 1) { status = Status.ERROR } /* Set the size for this download if it - hasn't been already set. */ + hasn't been already set. */ if (size == -1) { - size = contentLength; + size = contentLength } // Open file and seek to the end of it. - file = RandomAccessFile(dir + getFilename(url), "rw"); - file.seek(downloaded.toLong()); + file = RandomAccessFile(dir + getFilename(url), "rw") + file.seek(downloaded.toLong()) - stream = connection.getInputStream(); + stream = connection.getInputStream() while (status == Status.DOWNLOADING) { /* Size buffer according to how much of the - file is left to download. */ - val buffer: ByteArray; + file is left to download. */ + val buffer: ByteArray if (size - downloaded > MAX_BUFFER_SIZE as Int) { buffer = ByteArray(MAX_BUFFER_SIZE) } else { - buffer = ByteArray(size - downloaded); + buffer = ByteArray(size - downloaded) } // Read from server into buffer. - val read = stream.read(buffer); - if (read == -1) - break; + val read = stream.read(buffer) + if (read == -1) break // Write buffer to file. - file.write(buffer, 0, read); - downloaded += read; + file.write(buffer, 0, read) + downloaded += read } /* Change status to complete if this point was - reached because downloading has finished. */ + reached because downloading has finished. */ if (status == Status.DOWNLOADING) { - status = Status.COMPLETE; + status = Status.COMPLETE } } catch (e: Exception) { println(e) status = Status.ERROR } finally { - // Close file. - if (file != null) { - try { - file.close(); - } catch (e: Exception) {} - } + // Close file. + if (file != null) { + try { + file.close() + } catch (e: Exception) {} + } - // Close connection to server. - if (stream != null) { - try { - stream.close(); - } catch (e: Exception) {} - } + // Close connection to server. + if (stream != null) { + try { + stream.close() + } catch (e: Exception) {} + } } } } diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt index 141b477..8443eeb 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt @@ -1,7 +1,6 @@ package xyz.brysonsteck.ServerCraft.server import java.io.File -import java.io.InputStream import java.io.FileNotFoundException import java.util.Properties @@ -41,19 +40,34 @@ public class Server { var ins = File(dir + File.separator + "server.properties").inputStream() props.load(ins) try { - ins = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").inputStream() + ins = + File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties") + .inputStream() props.load(ins) } catch (e: FileNotFoundException) { if (convert) { - // create the file in question, as this is an external server being converted into a ServerCraft managed one - File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").createNewFile() + // create the file in question, as this is an external server being converted into a + // ServerCraft managed one + File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties") + .createNewFile() // also apply app-specific properties props.setProperty("jvm-ram", 1024.toString()) // then write to file val temp = Properties() - val outs = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").outputStream() + val outs = + File( + dir + + File.separator + + "ServerCraft" + + File.separator + + "ServerCraft.properties" + ) + .outputStream() temp.setProperty("jvm-ram", props.getProperty("jvm-ram")) - temp.store(outs, "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft") + temp.store( + outs, + "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft" + ) } else { println(e) } @@ -62,11 +76,19 @@ public class Server { private fun writeProps() { var outs = File(dir + File.separator + "server.properties").outputStream() - props.store(outs, "Minecraft server properties\nCreated with ServerCraft: https://codeberg.org/brysonsteck/ServerCraft") + props.store( + outs, + "Minecraft server properties\nCreated with ServerCraft: https://codeberg.org/brysonsteck/ServerCraft" + ) val temp = Properties() - outs = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").outputStream() + outs = + File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties") + .outputStream() temp.setProperty("jvm-ram", props.getProperty("jvm-ram")) - temp.store(outs, "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft") + temp.store( + outs, + "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft" + ) } public fun getProp(prop: String): String { @@ -77,5 +99,4 @@ public class Server { props.setProperty(key, value.toString()) writeProps() } - -} \ No newline at end of file +} diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/css/info-tabs.css b/src/main/resources/xyz/brysonsteck/ServerCraft/css/info-tabs.css index 023b0c1..ce78043 100644 --- a/src/main/resources/xyz/brysonsteck/ServerCraft/css/info-tabs.css +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/css/info-tabs.css @@ -2,4 +2,4 @@ -fx-background-color: "#F4F4F4"; -fx-border-color: "#DCDCDC"; -fx-border-width: 0 0 1 0 -} \ No newline at end of file +} diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/css/style.css b/src/main/resources/xyz/brysonsteck/ServerCraft/css/style.css new file mode 100644 index 0000000..0a606d1 --- /dev/null +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/css/style.css @@ -0,0 +1,4 @@ +.root{ + -fx-font-size: 11pt; + -fx-font-family: "Inter"; +} diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml b/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml index 21f2e5e..b51b8f8 100644 --- a/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml @@ -8,379 +8,374 @@ - - - - + + - + + - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +