aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryson Steck <brysonsteck@protonmail.com>2023-05-20 20:02:08 -0600
committerBryson Steck <brysonsteck@protonmail.com>2023-05-20 20:02:08 -0600
commit49311129e97a232074478c94db23a7285f106d99 (patch)
tree777ea947e6a4c68fb4248e21646374ec26f21cc4
parent9b200a05ef39c49a718d18829212e3f1fb350b67 (diff)
downloadServerCraft-49311129e97a232074478c94db23a7285f106d99.tar
ServerCraft-49311129e97a232074478c94db23a7285f106d99.tar.gz
ServerCraft-49311129e97a232074478c94db23a7285f106d99.tar.bz2
implemented properties, console, and logging system
-rw-r--r--build.gradle2
-rw-r--r--src/main/kotlin/xyz/brysonsteck/ServerCraft/controllers/PrimaryController.kt245
-rw-r--r--src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Download.kt6
-rw-r--r--src/main/kotlin/xyz/brysonsteck/ServerCraft/server/Server.kt24
-rw-r--r--src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.pngbin0 -> 846 bytes
-rw-r--r--src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.pngbin0 -> 841 bytes
-rw-r--r--src/main/resources/xyz/brysonsteck/ServerCraft/primary.fxml96
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
new file mode 100644
index 0000000..3ca5992
--- /dev/null
+++ b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_down.png
Binary files differ
diff --git a/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png
new file mode 100644
index 0000000..a61ea1d
--- /dev/null
+++ b/src/main/resources/xyz/brysonsteck/ServerCraft/icons/arrow_up.png
Binary files differ
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&#10;(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&#10;(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&#10;(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&#10;(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:&#10; " 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>