diff options
-rw-r--r-- | build.gradle | 2 | ||||
-rw-r--r-- | src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt | 245 | ||||
-rw-r--r-- | src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt | 6 | ||||
-rw-r--r-- | src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt | 24 | ||||
-rw-r--r-- | src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.png | bin | 0 -> 846 bytes | |||
-rw-r--r-- | src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png | bin | 0 -> 841 bytes | |||
-rw-r--r-- | src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml | 96 |
7 files changed, 298 insertions, 75 deletions
diff --git a/build.gradle b/build.gradle index 49bc3d4..3b908bf 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,7 @@ task pack(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: bu organizationUrl = "https://brysonsteck.xyz" organizationEmail = "me@brysonsteck.xyz" url = "https://codeberg.org/brysonsteck/ServerCraft" + additionalModules = [ "jdk.crypto.ec" ] linuxConfig { pngFile = file('src/main/resources/icon.png') @@ -113,7 +114,6 @@ task pack(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: bu } } -// tasks.register('fixAppImageIcon', Copy) { build.doLast { if (OperatingSystem.current().isLinux()) { exec { diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt index 99d1009..d445923 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt @@ -32,6 +32,7 @@ import javafx.scene.control.ButtonBar import javafx.scene.control.CheckBox 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 @@ -50,6 +51,7 @@ import javafx.stage.DirectoryChooser import javafx.stage.Modality import javafx.stage.Stage import javafx.event.EventHandler +import javafx.event.ActionEvent import org.rauschig.jarchivelib.* import xyz.brysonsteck.ServerCraft.server.Server @@ -58,6 +60,8 @@ import xyz.brysonsteck.ServerCraft.App class PrimaryController { @FXML + lateinit private var primary: Pane + @FXML lateinit private var currentDirectoryLabel: Label @FXML lateinit private var worldNameField: TextField @@ -94,7 +98,7 @@ class PrimaryController { @FXML lateinit private var playerCountCheckbox: CheckBox @FXML - lateinit private var maxPlayersSpinner: Spinner<kotlin.Int> + lateinit private var maxPlayerSpinner: Spinner<kotlin.Int> @FXML lateinit private var maxSizeSpinner: Spinner<kotlin.Int> @FXML @@ -117,15 +121,29 @@ class PrimaryController { 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 private var building = false private var directory = "" private var asyncResult = false private var started = false + private var loading = false + private var showingConsole = false + + private fun log(str: String) { + console.text = console.text + str + "\n" + println(str) + } @FXML public fun initialize() { + scrollPane.vvalueProperty().bind(console.heightProperty()); difficultyBox.items = FXCollections.observableArrayList( "Peaceful", "Easy", @@ -135,7 +153,9 @@ class PrimaryController { ) difficultyBox.value = "Normal" difficultyBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> - onChoiceBoxChange("difficulty", difficultyBox.items[new as Int]) + if (!loading) { + onPropChange("difficulty", difficultyBox.items[new as Int]) + } } gamemodeBox.items = FXCollections.observableArrayList( "Survival", @@ -145,7 +165,9 @@ class PrimaryController { ) gamemodeBox.value = "Survival" gamemodeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> - onChoiceBoxChange("gamemode", gamemodeBox.items[new as Int]) + if (!loading) { + onPropChange("gamemode", gamemodeBox.items[new as Int]) + } } worldTypeBox.items = FXCollections.observableArrayList( "Normal", @@ -155,7 +177,59 @@ class PrimaryController { ) worldTypeBox.value = "Normal" worldTypeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> - onChoiceBoxChange("world-type", worldTypeBox.items[new as Int]) + if (!loading) { + onPropChange("level-type", worldTypeBox.items[new as Int]) + } + } + maxPlayerSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("max-players", new) + } + } + maxSizeSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("max-world-size", new) + } + } + portSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("server-port", new) + } + } + renderSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("view-distance", new) + } + } + memorySpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("jvm-ram", new) + } + } + spawnSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("spawn-protection", new) + } + } + simulationSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("simulation-distance", new) + } + } + maxTickSpinner.editor.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("max-tick-time", new) + } + } + worldNameField.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("level-name", new) + } + } + seedField.textProperty().addListener { _, _, new -> + if (!loading) { + onPropChange("level-seed", new) + } } } @@ -174,6 +248,8 @@ class PrimaryController { worldSettingsPane.isDisable = false buildButton.isDisable = false defaultsButton.isDisable = false + applyProps() + server.dir = result.absolutePath } else { currentDirectoryLabel.text = "<NONE>" parentPane.isDisable = true @@ -185,33 +261,114 @@ class PrimaryController { } } - @FXML - private fun onWorldNameChange() { - + private fun parseBool(bool: String): Boolean { + if (bool == "true") { + return true + } + return false } - @FXML - private fun onSeedChange() { - + private fun applyProps() { + loading = true + flightCheckbox.isSelected = parseBool(server.getProp("allow-flight")) + netherCheckbox.isSelected = parseBool(server.getProp("allow-nether")) + structuresCheckbox.isSelected = parseBool(server.getProp("generate-structures")) + pvpCheckbox.isSelected = parseBool(server.getProp("pvp")) + whitelistCheckbox.isSelected = parseBool(server.getProp("white-list")) + cmdBlocksCheckbox.isSelected = parseBool(server.getProp("enable-command-block")) + playerCountCheckbox.isSelected = parseBool(server.getProp("hide-online-players")) + maxPlayerSpinner.valueFactory.value = server.getProp("max-players").toIntOrNull() + maxSizeSpinner.valueFactory.value = server.getProp("max-world-size").toIntOrNull() + portSpinner.valueFactory.value = server.getProp("server-port").toIntOrNull() + renderSpinner.valueFactory.value = server.getProp("view-distance").toIntOrNull() + memorySpinner.valueFactory.value = server.getProp("jvm-ram").toIntOrNull() + 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() } + } + gamemodeBox.value = server.getProp("gamemode").replaceFirstChar { it.uppercase() } + 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 } @FXML - private fun onPortChange() { - + private fun onCheckboxClick(e: ActionEvent) { + val box = e.target as CheckBox + when { + box == whitelistCheckbox -> { + server.setProp("white-list", whitelistCheckbox.isSelected) + } + box == pvpCheckbox -> { + server.setProp("pvp", pvpCheckbox.isSelected) + } + box == netherCheckbox -> { + server.setProp("allow-nether", netherCheckbox.isSelected) + } + box == cmdBlocksCheckbox -> { + server.setProp("enable-command-block", cmdBlocksCheckbox.isSelected) + } + box == flightCheckbox -> { + server.setProp("allow-flight", flightCheckbox.isSelected) + } + box == structuresCheckbox -> { + server.setProp("generate-structures", structuresCheckbox.isSelected) + } + box == playerCountCheckbox -> { + server.setProp("hide-online-players", playerCountCheckbox.isSelected) + } + } } - @FXML - private fun onCheckboxClick() { - + private fun onPropChange(prop: String, value: String) { + when { + prop == "gamemode" -> { + server.setProp(prop, value.lowercase()) + } + prop == "difficulty" -> { + if (value == "Hardcore") { + server.setProp("hardcode", "true") + server.setProp(prop, "hard") + } else { + server.setProp("hardcode", "false") + server.setProp(prop, value.lowercase()) + } + } + prop == "level-type" -> { + server.setProp(prop, "minecraft:" + value.lowercase().replace(" ", "_")) + } + else -> { + server.setProp(prop, value) + } + } } @FXML - private fun onSpinnerChange() { - + private fun onToggleConsole() { + if (showingConsole) { + primary.getScene().window.height = 743.0 + dropDownIcon.image = Image(App().javaClass.getResourceAsStream("icons/arrow_down.png")) + } else { + primary.getScene().window.height = 905.0 + dropDownIcon.image = Image(App().javaClass.getResourceAsStream("icons/arrow_up.png")) + } + showingConsole = !showingConsole } - private fun onChoiceBoxChange(box: String, selection: String) { - + @FXML + private fun onDefaults() { + val res = createDialog("info", "Reset settings to defaults?\nThere is NO GOING BACK!") + if (res) { + server.loadProps() + applyProps() + statusBar.text = "Resetting settings to defaults successful." + } } @FXML @@ -280,19 +437,21 @@ class PrimaryController { withContext(Dispatchers.JavaFx){ statusBar.text = "Downloading ${it.key}..." progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS + log("Downloading ${it.key} from ${it.value}") } val download = Download(URL(it.value), destinations[it.key]!!) 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 - print("") + withContext(Dispatchers.JavaFx) {log("Progress: ${prog * 100}%")} if (prog >= 0.01) { 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}")} } // extract java archive @@ -300,6 +459,7 @@ class PrimaryController { withContext(Dispatchers.JavaFx) { progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS statusBar.text = "Extracting Java archive..." + log("Extracting Java archive to ${directory + "ServerCraft" + File.separator + "Java"}") } var stream = archiver.stream(File(directory + "ServerCraft" + File.separator + "Java" + File.separator + javaFile)) val dest = File(directory + "ServerCraft" + File.separator + "Java") @@ -311,7 +471,10 @@ class PrimaryController { var entry = stream.getNextEntry() var currentEntry = 0.0 do { - withContext(Dispatchers.JavaFx) {progressBar.progress = currentEntry/entries} + withContext(Dispatchers.JavaFx) { + progressBar.progress = currentEntry/entries + log(entry.name) + } entry.extract(dest) entry = stream.getNextEntry() currentEntry++ @@ -335,7 +498,7 @@ class PrimaryController { if (!building) { proc.destroy() } - println(line) + withContext(Dispatchers.JavaFx) {log(line)} line = br.readLine() currentline++ if (currentline > 15) { @@ -343,7 +506,7 @@ class PrimaryController { } } } catch (e: IOException) { - println("Stream closed") + withContext(Dispatchers.JavaFx) {log("Stream Closed")} } } @@ -370,7 +533,7 @@ class PrimaryController { @FXML private fun onStart() { if (started) { - createDialog("warning", "You should only kill the server if\nabsolutely necessary. Data loss may occur.\nContinue anyway?", "Yes", "No", false) + createDialog("warning", "You should only kill the server if\nabsolutely necessary. Data loss may occur.\nContinue anyway?", hold=false) return; } if (!File(directory + "eula.txt").exists()) { @@ -391,7 +554,7 @@ class PrimaryController { startButton.text = "Kill Server" @Suppress("OPT_IN_USAGE") GlobalScope.launch(Dispatchers.Default) { - val builder = ProcessBuilder("java", "-jar", "${server.jar}") + val builder = ProcessBuilder("java", "-Xmx${server.getProp("jvm-ram")}M", "-jar", "${server.jar}") builder.directory(File(directory)) val proc = builder.start() val reader = InputStreamReader(proc.inputStream) @@ -406,11 +569,11 @@ class PrimaryController { } proc.destroy() } - println(line); + withContext(Dispatchers.JavaFx) {log(line)} line = br.readLine() } } catch (e: IOException) { - println("Stream closed") + withContext(Dispatchers.JavaFx) {log("Stream Closed")} } withContext(Dispatchers.JavaFx) { statusBar.text = if (asyncResult) { @@ -459,18 +622,18 @@ class PrimaryController { buttonBar.layoutY = 107.0 buttonBar.prefWidth = 400.0 val noButton = Button("I Disagree") - noButton.onMouseClicked = EventHandler<MouseEvent>() { + noButton.onAction = EventHandler<ActionEvent>() { result = false dialog.hide() } noButton.isDefaultButton = true val yesButton = Button("I Agree") - yesButton.onMouseClicked = EventHandler<MouseEvent>() { + yesButton.onAction = EventHandler<ActionEvent>() { result = true dialog.hide() } val eula = Button("View EULA") - eula.onMouseClicked = EventHandler<MouseEvent>() { + eula.onAction = EventHandler<ActionEvent>() { val desktop = Desktop.getDesktop() if (desktop.isSupported(Desktop.Action.BROWSE)) { // most likely running on Windows or macOS @@ -500,7 +663,7 @@ class PrimaryController { return result } - private fun createDialog(type: String, msg: String, yes: String, no: String, hold: Boolean): Boolean { + private fun createDialog(type: String, msg: String, yes: String = "Yes", no: String = "No", hold: Boolean = true): Boolean { var result = false val resources = App().javaClass.getResource("icons/$type.png") val dialog = Stage() @@ -527,7 +690,7 @@ class PrimaryController { buttonBar.layoutY = 107.0 buttonBar.prefWidth = 400.0 val noButton = Button(no) - noButton.onMouseClicked = EventHandler<MouseEvent>() { + noButton.onAction = EventHandler<ActionEvent>() { if (hold) { result = false } else { @@ -536,7 +699,7 @@ class PrimaryController { dialog.hide() } val yesButton = Button(yes) - yesButton.onMouseClicked = EventHandler<MouseEvent>() { + yesButton.onAction = EventHandler<ActionEvent>() { if (hold) { result = true } else { @@ -559,10 +722,12 @@ class PrimaryController { private fun loadServerDir(dir: String): Boolean { directory = dir + // exit if doesn't exist for whatever reason if (!File(directory).isDirectory) { return false; } + // add system dir separator for cleaner code if (directory[directory.length-1] != File.separatorChar) directory += File.separatorChar @@ -570,7 +735,7 @@ class PrimaryController { val hasProperties = File(directory + File.separator + "server.properties").isFile val hasServer = findServerJar() - if (hasDummy && hasServer) { + if (hasDummy && hasServer && hasProperties) { // server complete, just read jproperties statusBar.text = "Server found!" startButton.isDisable = false @@ -581,24 +746,32 @@ class PrimaryController { statusBar.text = "Server needs to be built before starting." } else if (!hasDummy && hasServer) { // server created externally - val result = createDialog("warning", "This server directory was not created by \nServerCraft. Errors may occur; copying\nthe world directories to a new folder may be\nsafer. Proceed anyway?", "Yes", "No", true) + val result = createDialog("warning", "This server directory was not created by \nServerCraft. Errors may occur; copying\nthe world directories to a new folder may be\nsafer. Proceed anyway?") statusBar.text = "Ready." if (result) { startButton.isDisable = false } + server.loadProps(dir) return result } else { // assume clean directory - val result = createDialog("info", "There is no server in this directory.\nCreate one?", "Yes", "No", true) + val result = createDialog("info", "There is no server in this directory.\nCreate one?") if (result) { File(directory + "ServerCraft").mkdir() startButton.isDisable = true buildButton.text = "Build Server" } statusBar.text = "Ready." + server.loadProps() return result } + if (hasProperties) { + server.loadProps(dir) + } else { + server.loadProps() + } + return true; } diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt index 757c32c..f538458 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt @@ -3,6 +3,7 @@ package xyz.brysonsteck.ServerCraft.server import java.io.*; import java.net.*; import java.util.*; +import javax.net.ssl.HttpsURLConnection class Download: Runnable { public enum class Status { @@ -43,7 +44,7 @@ class Download: Runnable { try { // Open connection to URL. - var connection = url.openConnection() as HttpURLConnection; + var connection = url.openConnection() as HttpsURLConnection; // Specify what portion of file to download. connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); @@ -53,12 +54,14 @@ class Download: Runnable { // Make sure response code is in the 200 range. if (connection.responseCode / 100 != 2) { + println(connection.responseCode) status = Status.ERROR } // Check for valid content length. contentLength = connection.getContentLength(); if (contentLength < 1) { + println(connection.getContentLength()) status = Status.ERROR } @@ -99,6 +102,7 @@ class Download: Runnable { status = Status.COMPLETE; } } catch (e: Exception) { + println(e) status = Status.ERROR } finally { // Close file. diff --git a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt index 746bc55..b591074 100644 --- a/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt +++ b/src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt @@ -1,14 +1,16 @@ package xyz.brysonsteck.ServerCraft.server import java.io.File +import java.io.InputStream import java.util.Properties public class Server { public var jar = "" + public var dir = "" private val props = Properties() - constructor() { + public fun loadProps() { props.setProperty("allow-flight", false.toString()) props.setProperty("allow-nether", true.toString()) props.setProperty("generate-structures", true.toString()) @@ -32,4 +34,24 @@ public class Server { props.setProperty("level-type", "minecraft:normal") props.setProperty("motd", "A server for a dummy") } + + public fun loadProps(dir: String) { + val ins = File(dir + File.separator + "server.properties").inputStream() + props.load(ins) + } + + private fun writeProps() { + val outs = File(dir + File.separator + "server.properties").outputStream() + props.store(outs, "Minecraft server properties\nCreated with ServerCraft: https://codeberg.org/brysonsteck/ServerCraft") + } + + public fun getProp(prop: String): String { + return props.getProperty(prop) + } + + public fun setProp(key: String, value: Any) { + props.setProperty(key, value.toString()) + writeProps() + } + }
\ No newline at end of file diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.png b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.png Binary files differnew file mode 100644 index 0000000..3ca5992 --- /dev/null +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.png diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png Binary files differnew file mode 100644 index 0000000..a61ea1d --- /dev/null +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml b/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml index 6b34102..cec0d2c 100644 --- a/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml +++ b/src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml @@ -9,22 +9,25 @@ <?import javafx.scene.control.Label?> <?import javafx.scene.control.MenuItem?> <?import javafx.scene.control.ProgressBar?> +<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.Separator?> <?import javafx.scene.control.Spinner?> <?import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory?> <?import javafx.scene.control.TextField?> <?import javafx.scene.control.TitledPane?> <?import javafx.scene.control.Tooltip?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.Pane?> <?import javafx.scene.text.Font?> -<Pane fx:id="primary" maxHeight="713.0" maxWidth="963.0" minHeight="713.0" minWidth="963.0" prefHeight="713.0" prefWidth="963.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="xyz.brysonsteck.ServerCraft.controllers.PrimaryController"> +<Pane fx:id="primary" maxHeight="873.0" maxWidth="963.0" minHeight="713.0" minWidth="963.0" prefHeight="873.0" prefWidth="963.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="xyz.brysonsteck.ServerCraft.controllers.PrimaryController"> <children> <HBox fx:id="directoryPane" prefHeight="39.0" prefWidth="963.0"> <children> - <Button id="openFile" fx:id="chooseDirectoryButton" lineSpacing="10.0" mnemonicParsing="false" onMouseClicked="#onDirectoryButtonClick" text="Choose Directory..."> + <Button id="openFile" fx:id="chooseDirectoryButton" lineSpacing="10.0" mnemonicParsing="false" onAction="#onDirectoryButtonClick" text="Choose Directory..."> <opaqueInsets> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </opaqueInsets> @@ -68,7 +71,7 @@ <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> </HBox.margin> </Label> - <TextField fx:id="worldNameField" onInputMethodTextChanged="#onWorldNameChange" text="world"> + <TextField fx:id="worldNameField" text="world"> <HBox.margin> <Insets top="2.0" /> </HBox.margin> @@ -86,7 +89,7 @@ <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> </HBox.margin> </Label> - <TextField fx:id="seedField" onInputMethodTextChanged="#onSeedChange" promptText="Leave empty for random seed" HBox.hgrow="ALWAYS"> + <TextField fx:id="seedField" promptText="Leave empty for random seed" HBox.hgrow="ALWAYS"> <HBox.margin> <Insets top="2.0" /> </HBox.margin> @@ -104,7 +107,7 @@ <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> </HBox.margin> </Label> - <Spinner fx:id="portSpinner" editable="true" onInputMethodTextChanged="#onPortChange" prefWidth="95.0"> + <Spinner fx:id="portSpinner" editable="true" prefWidth="95.0"> <HBox.margin> <Insets top="2.0" /> </HBox.margin> @@ -123,11 +126,11 @@ <content> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="311.0" prefWidth="625.0"> <children> - <CheckBox fx:id="flightCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Allow Flight" /> - <CheckBox fx:id="netherCheckbox" layoutX="14.0" layoutY="42.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" selected="true" text="Allow The Nether" /> - <CheckBox fx:id="structuresCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="70.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" selected="true" text="Generate Structures (such as villages and strongholds)" /> - <CheckBox fx:id="pvpCheckbox" layoutX="14.0" layoutY="109.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" selected="true" text="Allow PvP" /> - <CheckBox fx:id="whitelistCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="138.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Enable Whitelist (Only users you specify can join)" /> + <CheckBox fx:id="flightCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onAction="#onCheckboxClick" text="Allow Flight" /> + <CheckBox fx:id="netherCheckbox" layoutX="14.0" layoutY="42.0" mnemonicParsing="false" onAction="#onCheckboxClick" selected="true" text="Allow The Nether" /> + <CheckBox fx:id="structuresCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="70.0" mnemonicParsing="false" onAction="#onCheckboxClick" selected="true" text="Generate Structures (such as villages and strongholds)" /> + <CheckBox fx:id="pvpCheckbox" layoutX="14.0" layoutY="109.0" mnemonicParsing="false" onAction="#onCheckboxClick" selected="true" text="Allow PvP" /> + <CheckBox fx:id="whitelistCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="138.0" mnemonicParsing="false" onAction="#onCheckboxClick" text="Enable Whitelist (Only users you specify can join)" /> <HBox layoutX="6.0" layoutY="174.0"> <children> <Label text="Maximum Players:" HBox.hgrow="ALWAYS"> @@ -173,8 +176,8 @@ <content> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="269.0" prefWidth="625.0"> <children> - <CheckBox fx:id="cmdBlocksCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Enable Command Blocks" /> - <CheckBox fx:id="playerCountCheckbox" layoutX="14.0" layoutY="41.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Hide Online Player Count" /> + <CheckBox fx:id="cmdBlocksCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onAction="#onCheckboxClick" text="Enable Command Blocks" /> + <CheckBox fx:id="playerCountCheckbox" layoutX="14.0" layoutY="41.0" mnemonicParsing="false" onAction="#onCheckboxClick" text="Hide Online Player Count" /> <HBox layoutX="7.0" layoutY="65.0"> <children> <Label ellipsisString="" text="Server Memory in MB:" textOverrun="CLIP" HBox.hgrow="ALWAYS"> @@ -327,36 +330,57 @@ </Pane> <ButtonBar fx:id="buttonBar" buttonOrder="L+R" layoutY="635.0" prefHeight="40.0" prefWidth="963.0"> <buttons> - <Button fx:id="infoButton" mnemonicParsing="false" onMouseClicked="#onInfo" text="About ServerCraft" ButtonBar.buttonData="LEFT" /> - <Button fx:id="defaultsButton" disable="true" mnemonicParsing="false" onMouseClicked="#onBuild" text="Reset to Defaults" ButtonBar.buttonData="LEFT" /> - <Button fx:id="buildButton" disable="true" mnemonicParsing="false" onMouseClicked="#onBuild" text="Build Server" ButtonBar.buttonData="RIGHT" /> - <Button fx:id="startButton" defaultButton="true" disable="true" mnemonicParsing="false" onMouseClicked="#onStart" prefWidth="120.0" text="Start Server" ButtonBar.buttonData="RIGHT" /> + <Button fx:id="infoButton" mnemonicParsing="false" onAction="#onInfo" text="About ServerCraft" ButtonBar.buttonData="LEFT" /> + <Button fx:id="defaultsButton" disable="true" mnemonicParsing="false" onAction="#onDefaults" text="Reset to Defaults" ButtonBar.buttonData="LEFT" /> + <Button fx:id="buildButton" disable="true" mnemonicParsing="false" onAction="#onBuild" text="Build Server" ButtonBar.buttonData="RIGHT" /> + <Button fx:id="startButton" defaultButton="true" disable="true" mnemonicParsing="false" onAction="#onStart" prefWidth="120.0" text="Start Server" ButtonBar.buttonData="RIGHT" /> </buttons> <padding> <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" /> </padding> </ButtonBar> - <HBox layoutY="680.0" prefHeight="33.0" prefWidth="963.0" style="-fx-background-color: ddd;"> + <Pane layoutY="680.0" prefHeight="196.0" prefWidth="963.0" style="-fx-background-color: ddd;"> <children> - <Label text="Status:"> - <font> - <Font name="System Bold" size="13.0" /> - </font> - </Label> - <Label fx:id="statusBar" text="Ready."> - <HBox.margin> - <Insets left="5.0" /> - </HBox.margin> - </Label> - <ProgressBar fx:id="progressBar" prefWidth="400.0" visible="false"> - <HBox.margin> - <Insets left="10.0" /> - </HBox.margin> - </ProgressBar> + <HBox prefWidth="963.0"> + <children> + <Label text="Status:"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Label> + <Label fx:id="statusBar" text="Ready."> + <HBox.margin> + <Insets left="5.0" /> + </HBox.margin> + </Label> + <ProgressBar fx:id="progressBar" prefWidth="400.0" visible="false"> + <HBox.margin> + <Insets left="10.0" /> + </HBox.margin> + </ProgressBar> + </children> + <padding> + <Insets bottom="9.0" left="9.0" right="9.0" top="9.0" /> + </padding> + </HBox> + <ImageView fx:id="dropDownIcon" fitHeight="66.0" fitWidth="39.0" layoutX="923.0" layoutY="-3.0" onMouseClicked="#onToggleConsole" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@icons/arrow_down.png" /> + </image> + </ImageView> + <ScrollPane fx:id="scrollPane" layoutY="34.0" prefHeight="162.0" prefWidth="963.0"> + <padding> + <Insets bottom="7.0" left="7.0" right="7.0" top="7.0" /> + </padding> + <content> + <Label fx:id="console" prefWidth="935.0" text="Console Output: " wrapText="true"> + <font> + <Font name="Monospaced Regular" size="13.0" /> + </font> + </Label> + </content> + </ScrollPane> </children> - <padding> - <Insets bottom="9.0" left="9.0" right="9.0" top="9.0" /> - </padding> - </HBox> + </Pane> </children> </Pane> |