implemented properties, console, and logging system

This commit is contained in:
Bryson Steck 2023-05-20 20:02:08 -06:00
parent 9b200a05ef
commit 49311129e9
7 changed files with 299 additions and 76 deletions

View file

@ -89,6 +89,7 @@ task pack(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: bu
organizationUrl = "https://brysonsteck.xyz" organizationUrl = "https://brysonsteck.xyz"
organizationEmail = "me@brysonsteck.xyz" organizationEmail = "me@brysonsteck.xyz"
url = "https://codeberg.org/brysonsteck/ServerCraft" url = "https://codeberg.org/brysonsteck/ServerCraft"
additionalModules = [ "jdk.crypto.ec" ]
linuxConfig { linuxConfig {
pngFile = file('src/main/resources/icon.png') 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 { build.doLast {
if (OperatingSystem.current().isLinux()) { if (OperatingSystem.current().isLinux()) {
exec { exec {

View file

@ -32,6 +32,7 @@ import javafx.scene.control.ButtonBar
import javafx.scene.control.CheckBox import javafx.scene.control.CheckBox
import javafx.scene.control.ProgressBar import javafx.scene.control.ProgressBar
import javafx.scene.control.Hyperlink import javafx.scene.control.Hyperlink
import javafx.scene.control.ScrollPane
import javafx.scene.layout.Border import javafx.scene.layout.Border
import javafx.scene.layout.BorderStroke import javafx.scene.layout.BorderStroke
import javafx.scene.layout.GridPane import javafx.scene.layout.GridPane
@ -50,6 +51,7 @@ import javafx.stage.DirectoryChooser
import javafx.stage.Modality import javafx.stage.Modality
import javafx.stage.Stage import javafx.stage.Stage
import javafx.event.EventHandler import javafx.event.EventHandler
import javafx.event.ActionEvent
import org.rauschig.jarchivelib.* import org.rauschig.jarchivelib.*
import xyz.brysonsteck.ServerCraft.server.Server import xyz.brysonsteck.ServerCraft.server.Server
@ -57,6 +59,8 @@ import xyz.brysonsteck.ServerCraft.server.Download
import xyz.brysonsteck.ServerCraft.App import xyz.brysonsteck.ServerCraft.App
class PrimaryController { class PrimaryController {
@FXML
lateinit private var primary: Pane
@FXML @FXML
lateinit private var currentDirectoryLabel: Label lateinit private var currentDirectoryLabel: Label
@FXML @FXML
@ -94,7 +98,7 @@ class PrimaryController {
@FXML @FXML
lateinit private var playerCountCheckbox: CheckBox lateinit private var playerCountCheckbox: CheckBox
@FXML @FXML
lateinit private var maxPlayersSpinner: Spinner<kotlin.Int> lateinit private var maxPlayerSpinner: Spinner<kotlin.Int>
@FXML @FXML
lateinit private var maxSizeSpinner: Spinner<kotlin.Int> lateinit private var maxSizeSpinner: Spinner<kotlin.Int>
@FXML @FXML
@ -117,15 +121,29 @@ class PrimaryController {
lateinit private var buildButton: Button lateinit private var buildButton: Button
@FXML @FXML
lateinit private var defaultsButton: Button 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 server: Server
private var building = false private var building = false
private var directory = "" private var directory = ""
private var asyncResult = false private var asyncResult = false
private var started = 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 @FXML
public fun initialize() { public fun initialize() {
scrollPane.vvalueProperty().bind(console.heightProperty());
difficultyBox.items = FXCollections.observableArrayList( difficultyBox.items = FXCollections.observableArrayList(
"Peaceful", "Peaceful",
"Easy", "Easy",
@ -135,7 +153,9 @@ class PrimaryController {
) )
difficultyBox.value = "Normal" difficultyBox.value = "Normal"
difficultyBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> 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( gamemodeBox.items = FXCollections.observableArrayList(
"Survival", "Survival",
@ -145,7 +165,9 @@ class PrimaryController {
) )
gamemodeBox.value = "Survival" gamemodeBox.value = "Survival"
gamemodeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> 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( worldTypeBox.items = FXCollections.observableArrayList(
"Normal", "Normal",
@ -155,7 +177,59 @@ class PrimaryController {
) )
worldTypeBox.value = "Normal" worldTypeBox.value = "Normal"
worldTypeBox.selectionModel.selectedIndexProperty().addListener { _, _, new -> 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 worldSettingsPane.isDisable = false
buildButton.isDisable = false buildButton.isDisable = false
defaultsButton.isDisable = false defaultsButton.isDisable = false
applyProps()
server.dir = result.absolutePath
} else { } else {
currentDirectoryLabel.text = "<NONE>" currentDirectoryLabel.text = "<NONE>"
parentPane.isDisable = true parentPane.isDisable = true
@ -185,33 +261,114 @@ class PrimaryController {
} }
} }
@FXML private fun parseBool(bool: String): Boolean {
private fun onWorldNameChange() { if (bool == "true") {
return true
}
return false
}
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 @FXML
private fun onSeedChange() { 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)
}
}
}
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 @FXML
private fun onPortChange() { 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
} }
@FXML @FXML
private fun onCheckboxClick() { 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
private fun onSpinnerChange() {
}
private fun onChoiceBoxChange(box: String, selection: String) {
} }
@FXML @FXML
@ -280,19 +437,21 @@ class PrimaryController {
withContext(Dispatchers.JavaFx){ withContext(Dispatchers.JavaFx){
statusBar.text = "Downloading ${it.key}..." statusBar.text = "Downloading ${it.key}..."
progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS
log("Downloading ${it.key} from ${it.value}")
} }
val download = Download(URL(it.value), destinations[it.key]!!) val download = Download(URL(it.value), destinations[it.key]!!)
download.start() download.start()
while (download.status == Download.Status.DOWNLOADING) { while (download.status == Download.Status.DOWNLOADING) {
var prog = (download.downloaded.toDouble() / download.contentLength.toDouble()) 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 // 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) { if (prog >= 0.01) {
withContext(Dispatchers.JavaFx) {progressBar.progress = prog} withContext(Dispatchers.JavaFx) {progressBar.progress = prog}
} }
if (!building) download.status = Download.Status.CANCELLED if (!building) download.status = Download.Status.CANCELLED
Thread.sleep(300) Thread.sleep(300)
} }
withContext(Dispatchers.JavaFx) {log("Download of ${it.key} complete with status: ${download.status}")}
} }
// extract java archive // extract java archive
@ -300,6 +459,7 @@ class PrimaryController {
withContext(Dispatchers.JavaFx) { withContext(Dispatchers.JavaFx) {
progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS
statusBar.text = "Extracting Java archive..." 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)) var stream = archiver.stream(File(directory + "ServerCraft" + File.separator + "Java" + File.separator + javaFile))
val dest = File(directory + "ServerCraft" + File.separator + "Java") val dest = File(directory + "ServerCraft" + File.separator + "Java")
@ -311,7 +471,10 @@ class PrimaryController {
var entry = stream.getNextEntry() var entry = stream.getNextEntry()
var currentEntry = 0.0 var currentEntry = 0.0
do { do {
withContext(Dispatchers.JavaFx) {progressBar.progress = currentEntry/entries} withContext(Dispatchers.JavaFx) {
progressBar.progress = currentEntry/entries
log(entry.name)
}
entry.extract(dest) entry.extract(dest)
entry = stream.getNextEntry() entry = stream.getNextEntry()
currentEntry++ currentEntry++
@ -335,7 +498,7 @@ class PrimaryController {
if (!building) { if (!building) {
proc.destroy() proc.destroy()
} }
println(line) withContext(Dispatchers.JavaFx) {log(line)}
line = br.readLine() line = br.readLine()
currentline++ currentline++
if (currentline > 15) { if (currentline > 15) {
@ -343,7 +506,7 @@ class PrimaryController {
} }
} }
} catch (e: IOException) { } catch (e: IOException) {
println("Stream closed") withContext(Dispatchers.JavaFx) {log("Stream Closed")}
} }
} }
@ -370,7 +533,7 @@ class PrimaryController {
@FXML @FXML
private fun onStart() { private fun onStart() {
if (started) { 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; return;
} }
if (!File(directory + "eula.txt").exists()) { if (!File(directory + "eula.txt").exists()) {
@ -391,7 +554,7 @@ class PrimaryController {
startButton.text = "Kill Server" startButton.text = "Kill Server"
@Suppress("OPT_IN_USAGE") @Suppress("OPT_IN_USAGE")
GlobalScope.launch(Dispatchers.Default) { 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)) builder.directory(File(directory))
val proc = builder.start() val proc = builder.start()
val reader = InputStreamReader(proc.inputStream) val reader = InputStreamReader(proc.inputStream)
@ -406,11 +569,11 @@ class PrimaryController {
} }
proc.destroy() proc.destroy()
} }
println(line); withContext(Dispatchers.JavaFx) {log(line)}
line = br.readLine() line = br.readLine()
} }
} catch (e: IOException) { } catch (e: IOException) {
println("Stream closed") withContext(Dispatchers.JavaFx) {log("Stream Closed")}
} }
withContext(Dispatchers.JavaFx) { withContext(Dispatchers.JavaFx) {
statusBar.text = if (asyncResult) { statusBar.text = if (asyncResult) {
@ -459,18 +622,18 @@ class PrimaryController {
buttonBar.layoutY = 107.0 buttonBar.layoutY = 107.0
buttonBar.prefWidth = 400.0 buttonBar.prefWidth = 400.0
val noButton = Button("I Disagree") val noButton = Button("I Disagree")
noButton.onMouseClicked = EventHandler<MouseEvent>() { noButton.onAction = EventHandler<ActionEvent>() {
result = false result = false
dialog.hide() dialog.hide()
} }
noButton.isDefaultButton = true noButton.isDefaultButton = true
val yesButton = Button("I Agree") val yesButton = Button("I Agree")
yesButton.onMouseClicked = EventHandler<MouseEvent>() { yesButton.onAction = EventHandler<ActionEvent>() {
result = true result = true
dialog.hide() dialog.hide()
} }
val eula = Button("View EULA") val eula = Button("View EULA")
eula.onMouseClicked = EventHandler<MouseEvent>() { eula.onAction = EventHandler<ActionEvent>() {
val desktop = Desktop.getDesktop() val desktop = Desktop.getDesktop()
if (desktop.isSupported(Desktop.Action.BROWSE)) { if (desktop.isSupported(Desktop.Action.BROWSE)) {
// most likely running on Windows or macOS // most likely running on Windows or macOS
@ -500,7 +663,7 @@ class PrimaryController {
return result 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 var result = false
val resources = App().javaClass.getResource("icons/$type.png") val resources = App().javaClass.getResource("icons/$type.png")
val dialog = Stage() val dialog = Stage()
@ -527,7 +690,7 @@ class PrimaryController {
buttonBar.layoutY = 107.0 buttonBar.layoutY = 107.0
buttonBar.prefWidth = 400.0 buttonBar.prefWidth = 400.0
val noButton = Button(no) val noButton = Button(no)
noButton.onMouseClicked = EventHandler<MouseEvent>() { noButton.onAction = EventHandler<ActionEvent>() {
if (hold) { if (hold) {
result = false result = false
} else { } else {
@ -536,7 +699,7 @@ class PrimaryController {
dialog.hide() dialog.hide()
} }
val yesButton = Button(yes) val yesButton = Button(yes)
yesButton.onMouseClicked = EventHandler<MouseEvent>() { yesButton.onAction = EventHandler<ActionEvent>() {
if (hold) { if (hold) {
result = true result = true
} else { } else {
@ -559,10 +722,12 @@ class PrimaryController {
private fun loadServerDir(dir: String): Boolean { private fun loadServerDir(dir: String): Boolean {
directory = dir directory = dir
// exit if doesn't exist for whatever reason
if (!File(directory).isDirectory) { if (!File(directory).isDirectory) {
return false; return false;
} }
// add system dir separator for cleaner code
if (directory[directory.length-1] != File.separatorChar) if (directory[directory.length-1] != File.separatorChar)
directory += File.separatorChar directory += File.separatorChar
@ -570,7 +735,7 @@ class PrimaryController {
val hasProperties = File(directory + File.separator + "server.properties").isFile val hasProperties = File(directory + File.separator + "server.properties").isFile
val hasServer = findServerJar() val hasServer = findServerJar()
if (hasDummy && hasServer) { if (hasDummy && hasServer && hasProperties) {
// server complete, just read jproperties // server complete, just read jproperties
statusBar.text = "Server found!" statusBar.text = "Server found!"
startButton.isDisable = false startButton.isDisable = false
@ -581,24 +746,32 @@ class PrimaryController {
statusBar.text = "Server needs to be built before starting." statusBar.text = "Server needs to be built before starting."
} else if (!hasDummy && hasServer) { } else if (!hasDummy && hasServer) {
// server created externally // 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." statusBar.text = "Ready."
if (result) { if (result) {
startButton.isDisable = false startButton.isDisable = false
} }
server.loadProps(dir)
return result return result
} else { } else {
// assume clean directory // 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) { if (result) {
File(directory + "ServerCraft").mkdir() File(directory + "ServerCraft").mkdir()
startButton.isDisable = true startButton.isDisable = true
buildButton.text = "Build Server" buildButton.text = "Build Server"
} }
statusBar.text = "Ready." statusBar.text = "Ready."
server.loadProps()
return result return result
} }
if (hasProperties) {
server.loadProps(dir)
} else {
server.loadProps()
}
return true; return true;
} }

View file

@ -3,6 +3,7 @@ package xyz.brysonsteck.ServerCraft.server
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import javax.net.ssl.HttpsURLConnection
class Download: Runnable { class Download: Runnable {
public enum class Status { public enum class Status {
@ -43,7 +44,7 @@ class Download: Runnable {
try { try {
// Open connection to URL. // Open connection to URL.
var connection = url.openConnection() as HttpURLConnection; var connection = url.openConnection() as HttpsURLConnection;
// Specify what portion of file to download. // Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-"); connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
@ -53,12 +54,14 @@ class Download: Runnable {
// Make sure response code is in the 200 range. // Make sure response code is in the 200 range.
if (connection.responseCode / 100 != 2) { if (connection.responseCode / 100 != 2) {
println(connection.responseCode)
status = Status.ERROR status = Status.ERROR
} }
// Check for valid content length. // Check for valid content length.
contentLength = connection.getContentLength(); contentLength = connection.getContentLength();
if (contentLength < 1) { if (contentLength < 1) {
println(connection.getContentLength())
status = Status.ERROR status = Status.ERROR
} }
@ -99,6 +102,7 @@ class Download: Runnable {
status = Status.COMPLETE; status = Status.COMPLETE;
} }
} catch (e: Exception) { } catch (e: Exception) {
println(e)
status = Status.ERROR status = Status.ERROR
} finally { } finally {
// Close file. // Close file.

View file

@ -1,14 +1,16 @@
package xyz.brysonsteck.ServerCraft.server package xyz.brysonsteck.ServerCraft.server
import java.io.File import java.io.File
import java.io.InputStream
import java.util.Properties import java.util.Properties
public class Server { public class Server {
public var jar = "" public var jar = ""
public var dir = ""
private val props = Properties() private val props = Properties()
constructor() { public fun loadProps() {
props.setProperty("allow-flight", false.toString()) props.setProperty("allow-flight", false.toString())
props.setProperty("allow-nether", true.toString()) props.setProperty("allow-nether", true.toString())
props.setProperty("generate-structures", true.toString()) props.setProperty("generate-structures", true.toString())
@ -32,4 +34,24 @@ public class Server {
props.setProperty("level-type", "minecraft:normal") props.setProperty("level-type", "minecraft:normal")
props.setProperty("motd", "A server for a dummy") 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()
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View file

@ -9,22 +9,25 @@
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.MenuItem?> <?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ProgressBar?> <?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Separator?> <?import javafx.scene.control.Separator?>
<?import javafx.scene.control.Spinner?> <?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory?> <?import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory?>
<?import javafx.scene.control.TextField?> <?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?> <?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.Tooltip?> <?import javafx.scene.control.Tooltip?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?> <?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> <children>
<HBox fx:id="directoryPane" prefHeight="39.0" prefWidth="963.0"> <HBox fx:id="directoryPane" prefHeight="39.0" prefWidth="963.0">
<children> <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> <opaqueInsets>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</opaqueInsets> </opaqueInsets>
@ -68,7 +71,7 @@
<Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
</HBox.margin> </HBox.margin>
</Label> </Label>
<TextField fx:id="worldNameField" onInputMethodTextChanged="#onWorldNameChange" text="world"> <TextField fx:id="worldNameField" text="world">
<HBox.margin> <HBox.margin>
<Insets top="2.0" /> <Insets top="2.0" />
</HBox.margin> </HBox.margin>
@ -86,7 +89,7 @@
<Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
</HBox.margin> </HBox.margin>
</Label> </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> <HBox.margin>
<Insets top="2.0" /> <Insets top="2.0" />
</HBox.margin> </HBox.margin>
@ -104,7 +107,7 @@
<Insets bottom="5.0" left="5.0" right="5.0" top="6.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
</HBox.margin> </HBox.margin>
</Label> </Label>
<Spinner fx:id="portSpinner" editable="true" onInputMethodTextChanged="#onPortChange" prefWidth="95.0"> <Spinner fx:id="portSpinner" editable="true" prefWidth="95.0">
<HBox.margin> <HBox.margin>
<Insets top="2.0" /> <Insets top="2.0" />
</HBox.margin> </HBox.margin>
@ -123,11 +126,11 @@
<content> <content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="311.0" prefWidth="625.0"> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="311.0" prefWidth="625.0">
<children> <children>
<CheckBox fx:id="flightCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Allow Flight" /> <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" onMouseClicked="#onCheckboxClick" selected="true" text="Allow The Nether" /> <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" onMouseClicked="#onCheckboxClick" selected="true" text="Generate Structures&#10;(such as villages and strongholds)" /> <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" onMouseClicked="#onCheckboxClick" selected="true" text="Allow PvP" /> <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" onMouseClicked="#onCheckboxClick" text="Enable Whitelist&#10;(Only users you specify can join)" /> <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"> <HBox layoutX="6.0" layoutY="174.0">
<children> <children>
<Label text="Maximum Players:" HBox.hgrow="ALWAYS"> <Label text="Maximum Players:" HBox.hgrow="ALWAYS">
@ -173,8 +176,8 @@
<content> <content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="269.0" prefWidth="625.0"> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="269.0" prefWidth="625.0">
<children> <children>
<CheckBox fx:id="cmdBlocksCheckbox" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Enable Command Blocks" /> <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" onMouseClicked="#onCheckboxClick" text="Hide Online Player Count" /> <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"> <HBox layoutX="7.0" layoutY="65.0">
<children> <children>
<Label ellipsisString="" text="Server Memory in MB:" textOverrun="CLIP" HBox.hgrow="ALWAYS"> <Label ellipsisString="" text="Server Memory in MB:" textOverrun="CLIP" HBox.hgrow="ALWAYS">
@ -327,16 +330,18 @@
</Pane> </Pane>
<ButtonBar fx:id="buttonBar" buttonOrder="L+R" layoutY="635.0" prefHeight="40.0" prefWidth="963.0"> <ButtonBar fx:id="buttonBar" buttonOrder="L+R" layoutY="635.0" prefHeight="40.0" prefWidth="963.0">
<buttons> <buttons>
<Button fx:id="infoButton" mnemonicParsing="false" onMouseClicked="#onInfo" text="About ServerCraft" ButtonBar.buttonData="LEFT" /> <Button fx:id="infoButton" mnemonicParsing="false" onAction="#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="defaultsButton" disable="true" mnemonicParsing="false" onAction="#onDefaults" 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="buildButton" disable="true" mnemonicParsing="false" onAction="#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="startButton" defaultButton="true" disable="true" mnemonicParsing="false" onAction="#onStart" prefWidth="120.0" text="Start Server" ButtonBar.buttonData="RIGHT" />
</buttons> </buttons>
<padding> <padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" /> <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</padding> </padding>
</ButtonBar> </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>
<HBox prefWidth="963.0">
<children> <children>
<Label text="Status:"> <Label text="Status:">
<font> <font>
@ -358,5 +363,24 @@
<Insets bottom="9.0" left="9.0" right="9.0" top="9.0" /> <Insets bottom="9.0" left="9.0" right="9.0" top="9.0" />
</padding> </padding>
</HBox> </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>
</Pane>
</children> </children>
</Pane> </Pane>