Compare commits
No commits in common. "9a16ada0f604eb409258efc1d8f2881d09634f4f" and "633132c1c058a9e7bd994b350b775e1b9aa779ab" have entirely different histories.
9a16ada0f6
...
633132c1c0
17 changed files with 183 additions and 386 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -7,6 +7,3 @@ bin
|
||||||
|
|
||||||
# Ignore VSCode settings
|
# Ignore VSCode settings
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# ignore local releases dir for Codeberg releases
|
|
||||||
releases
|
|
||||||
|
|
34
README.md
34
README.md
|
@ -4,42 +4,14 @@ Is the `server.properties` file too overwhelming? Here is a graphical applicatio
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
ServerCraft is designed for the layman who has knowledge of Minecraft but has no idea how to create a simple server. It provides a simple interface for running a Minecraft server and managing it's settings, all automated with simple clicks.
|
ServerCraft is designed for the layman who has knowledge of Minecraft but has no idea how to create a simple server.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Utilizes the Spigot Minecraft server for performance and extensibility
|
* Utilizes the Spigot Minecraft server for performance and extensibility
|
||||||
* Automatically downloads the correct Java version, no more confusing errors when trying to run a server
|
* Automatically downloads the correct Java version, no more confusing errors when trying to run the server
|
||||||
* Common settings available at a glance
|
* Common settings available at a glance
|
||||||
* Separate section for common settings that are a little more advanced, such as:
|
* Separate section for common settings that are a little more advanced, such as:
|
||||||
* The amount of RAM to allocate to the server
|
* The amount of RAM to allocate to the server
|
||||||
* Render and simulation distances
|
* Render and simulation distances
|
||||||
* Enabling command blocks
|
*
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
provide screenshots
|
|
||||||
|
|
||||||
## Building and Running
|
|
||||||
|
|
||||||
Pre-compiled binaries are available to download [here](https://codeberg.org/brysonsteck/ServerCraft/releases). Simply download the correct file for your OS. (`.exe` or `.msi` for Windows, `.dmg` or `.pkg` for macOS, etc.)
|
|
||||||
|
|
||||||
ServerCraft uses Gradle as the build system of choice. At the very least, you will need to install at least Java 11. ServerCraft is built on OpenJFX, of which requires Java 11 or later. The binaries I offer on the [releases page](https://codeberg.org/brysonsteck/ServerCraft/releases) are bundled with Java 17 and do not require installing Java to run.
|
|
||||||
|
|
||||||
However, installing Gradle is not necessary. You can replace `gradle` in the following instructions with `./gradlew` on Unix systems and `.\gradlew.bat` on Windows systems, as long as your current directory is the root of the repository. These scripts download and run a Gradle jar file.
|
|
||||||
|
|
||||||
To build ServerCraft from source, creating a fat jar, Java class files, etc., run:
|
|
||||||
```
|
|
||||||
gradle build
|
|
||||||
```
|
|
||||||
|
|
||||||
To run ServerCraft from source, run:
|
|
||||||
```
|
|
||||||
gradle run
|
|
||||||
```
|
|
||||||
|
|
||||||
To compile binaries for the current system, run:
|
|
||||||
```
|
|
||||||
gradle pack
|
|
||||||
```
|
|
||||||
**NOTE**: Building binaries on Windows requires the WiX toolset and Inno Setup to be installed and on the PATH. See this JavaPackager [GitHub page](https://github.com/fvarrui/JavaPackager/blob/master/docs/windows-tools-guide.md) for installation instructions.
|
|
||||||
|
|
36
build.gradle
36
build.gradle
|
@ -32,6 +32,8 @@ plugins {
|
||||||
|
|
||||||
apply plugin: 'io.github.fvarrui.javapackager.plugin'
|
apply plugin: 'io.github.fvarrui.javapackager.plugin'
|
||||||
|
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
// Use Maven Central for resolving dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -66,6 +68,8 @@ distTar {
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
// archiveFileName = 'ServerCraft.jar'
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes 'Main-Class': application.mainClass
|
attributes 'Main-Class': application.mainClass
|
||||||
}
|
}
|
||||||
|
@ -85,7 +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 = [ "java.management", "jdk.crypto.ec" ]
|
additionalModules = [ "jdk.crypto.ec" ]
|
||||||
|
|
||||||
linuxConfig {
|
linuxConfig {
|
||||||
pngFile = file('src/main/resources/icon.png')
|
pngFile = file('src/main/resources/icon.png')
|
||||||
|
@ -110,32 +114,6 @@ task pack(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: bu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task createProperties(dependsOn: processResources) {
|
|
||||||
doLast {
|
|
||||||
def stdout = new ByteArrayOutputStream()
|
|
||||||
|
|
||||||
if (project.hasProperty("release")) {
|
|
||||||
exec {
|
|
||||||
commandLine "git", "describe", "--tags", "--abbrev=0"
|
|
||||||
standardOutput = stdout
|
|
||||||
}
|
|
||||||
project.version = stdout.toString().trim().replaceAll("\"", "")
|
|
||||||
} else {
|
|
||||||
exec {
|
|
||||||
commandLine "git", "log", "-n", "1", "--pretty=format:\"%h\""
|
|
||||||
standardOutput = stdout
|
|
||||||
}
|
|
||||||
project.version = "git(" + stdout.toString().trim().replaceAll("\"", "") + ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
new File("$buildDir/resources/main/xyz/brysonsteck/ServerCraft/info.properties").withWriter { w ->
|
|
||||||
Properties p = new Properties()
|
|
||||||
p['version'] = project.version.toString()
|
|
||||||
p.store w, null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build.doLast {
|
build.doLast {
|
||||||
if (OperatingSystem.current().isLinux()) {
|
if (OperatingSystem.current().isLinux()) {
|
||||||
exec {
|
exec {
|
||||||
|
@ -149,10 +127,6 @@ build.doLast {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classes {
|
|
||||||
dependsOn createProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
javafx {
|
javafx {
|
||||||
version = "20"
|
version = "20"
|
||||||
modules = ['javafx.controls', 'javafx.fxml', 'javafx.graphics']
|
modules = ['javafx.controls', 'javafx.fxml', 'javafx.graphics']
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
# Features
|
|
||||||
|
|
||||||
Here are all the features currently implemented/planned for ServerCraft:
|
|
||||||
|
|
||||||
* [X] Change `server.properties` file
|
|
||||||
* [X] Adapt controls based on what properties are found
|
|
||||||
* [X] Save server-specific settings in subdirectory
|
|
||||||
* [X] Allow changing memory passed into JVM
|
|
||||||
* [ ] Manage port forwarding automatically using UPnP
|
|
||||||
* [ ] Create system to check for updates
|
|
||||||
* [ ] Add and remove Spigot server mods/plugins
|
|
||||||
* [ ] Create servers of any Minecraft version >= 1.8
|
|
||||||
* [ ] "Advanced mode" with all settings editable
|
|
||||||
* [ ] Recognize existing server jars and give option to run them instead
|
|
|
@ -4,18 +4,12 @@ import javafx.fxml.FXML
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.control.Hyperlink
|
import javafx.scene.control.Hyperlink
|
||||||
import javafx.scene.control.Label
|
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import javafx.event.ActionEvent
|
import javafx.event.ActionEvent
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.Properties
|
|
||||||
import xyz.brysonsteck.ServerCraft.App
|
|
||||||
|
|
||||||
class InfoController {
|
class InfoController {
|
||||||
@FXML
|
|
||||||
lateinit private var version: Label
|
|
||||||
|
|
||||||
private val emails = mapOf(
|
private val emails = mapOf(
|
||||||
"bryson" to "me@brysonsteck.xyz"
|
"bryson" to "me@brysonsteck.xyz"
|
||||||
)
|
)
|
||||||
|
@ -25,13 +19,6 @@ class InfoController {
|
||||||
private val source = "https://codeberg.org/brysonsteck/ServerCraft"
|
private val source = "https://codeberg.org/brysonsteck/ServerCraft"
|
||||||
private val license = "https://www.gnu.org/licenses/gpl-3.0.html"
|
private val license = "https://www.gnu.org/licenses/gpl-3.0.html"
|
||||||
|
|
||||||
@FXML
|
|
||||||
public fun initialize() {
|
|
||||||
val props = Properties()
|
|
||||||
props.load(App().javaClass.getResourceAsStream("info.properties"))
|
|
||||||
version.text += props.getProperty("version")
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private fun openHyperlink(e: ActionEvent) {
|
private fun openHyperlink(e: ActionEvent) {
|
||||||
val link = e.source as Hyperlink
|
val link = e.source as Hyperlink
|
||||||
|
@ -65,6 +52,7 @@ class InfoController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
split[0].equals("license") -> {
|
split[0].equals("license") -> {
|
||||||
|
println("license")
|
||||||
if (!os.contains("linux")) {
|
if (!os.contains("linux")) {
|
||||||
desktop.browse(URI(license))
|
desktop.browse(URI(license))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -57,10 +57,6 @@ import org.rauschig.jarchivelib.*
|
||||||
import xyz.brysonsteck.ServerCraft.server.Server
|
import xyz.brysonsteck.ServerCraft.server.Server
|
||||||
import xyz.brysonsteck.ServerCraft.server.Download
|
import xyz.brysonsteck.ServerCraft.server.Download
|
||||||
import xyz.brysonsteck.ServerCraft.App
|
import xyz.brysonsteck.ServerCraft.App
|
||||||
import xyz.brysonsteck.ServerCraft.dialogs.Dialog
|
|
||||||
import xyz.brysonsteck.ServerCraft.dialogs.CommonDialog
|
|
||||||
import xyz.brysonsteck.ServerCraft.dialogs.KillDialog
|
|
||||||
import xyz.brysonsteck.ServerCraft.dialogs.EulaDialog
|
|
||||||
|
|
||||||
class PrimaryController {
|
class PrimaryController {
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -133,19 +129,16 @@ class PrimaryController {
|
||||||
lateinit private var scrollPane: ScrollPane
|
lateinit private var scrollPane: ScrollPane
|
||||||
|
|
||||||
lateinit private var server: Server
|
lateinit private var server: Server
|
||||||
lateinit private var killDialog: Dialog
|
|
||||||
private var building = false
|
private var building = false
|
||||||
private var directory = ""
|
private var directory = ""
|
||||||
|
private var asyncResult = false
|
||||||
private var started = false
|
private var started = false
|
||||||
private var loading = false
|
private var loading = false
|
||||||
private var showingConsole = false
|
private var showingConsole = false
|
||||||
|
|
||||||
private fun log(str: String) {
|
private fun log(str: String) {
|
||||||
if (console.text.length > 100000) {
|
console.text = console.text + str + "\n"
|
||||||
console.text = console.text.drop(str.length + 10)
|
println(str)
|
||||||
}
|
|
||||||
console.text = console.text + str
|
|
||||||
print(str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -238,7 +231,6 @@ class PrimaryController {
|
||||||
onPropChange("level-seed", new)
|
onPropChange("level-seed", new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
killDialog = KillDialog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -341,10 +333,10 @@ class PrimaryController {
|
||||||
}
|
}
|
||||||
prop == "difficulty" -> {
|
prop == "difficulty" -> {
|
||||||
if (value == "Hardcore") {
|
if (value == "Hardcore") {
|
||||||
server.setProp("hardcore", "true")
|
server.setProp("hardcode", "true")
|
||||||
server.setProp(prop, "hard")
|
server.setProp(prop, "hard")
|
||||||
} else {
|
} else {
|
||||||
server.setProp("hardcore", "false")
|
server.setProp("hardcode", "false")
|
||||||
server.setProp(prop, value.lowercase())
|
server.setProp(prop, value.lowercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +365,7 @@ class PrimaryController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private fun onDefaults() {
|
private fun onDefaults() {
|
||||||
val res = CommonDialog("info", "Reset all settings?", "Reset settings to defaults?\nThere is NO GOING BACK!").show()
|
val res = createDialog("info", "Reset settings to defaults?\nThere is NO GOING BACK!")
|
||||||
if (res) {
|
if (res) {
|
||||||
server.loadProps()
|
server.loadProps()
|
||||||
applyProps()
|
applyProps()
|
||||||
|
@ -449,21 +441,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}\n")
|
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
|
||||||
withContext(Dispatchers.JavaFx) {log("Progress: ${prog * 100}%\n")}
|
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}\n")}
|
withContext(Dispatchers.JavaFx) {log("Download of ${it.key} complete with status: ${download.status}")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract java archive
|
// extract java archive
|
||||||
|
@ -471,7 +463,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"}\n")
|
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")
|
||||||
|
@ -485,7 +477,7 @@ class PrimaryController {
|
||||||
do {
|
do {
|
||||||
withContext(Dispatchers.JavaFx) {
|
withContext(Dispatchers.JavaFx) {
|
||||||
progressBar.progress = currentEntry/entries
|
progressBar.progress = currentEntry/entries
|
||||||
log(entry.name + "\n")
|
log(entry.name)
|
||||||
}
|
}
|
||||||
entry.extract(dest)
|
entry.extract(dest)
|
||||||
entry = stream.getNextEntry()
|
entry = stream.getNextEntry()
|
||||||
|
@ -498,35 +490,31 @@ class PrimaryController {
|
||||||
progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS
|
progressBar.progress = ProgressBar.INDETERMINATE_PROGRESS
|
||||||
statusBar.text = "Building Minecraft Server..."
|
statusBar.text = "Building Minecraft Server..."
|
||||||
}
|
}
|
||||||
val builder = ProcessBuilder("${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", "-jar", "BuildTools.jar", "--rev", "latest", "-o", ".." + File.separator + ".." + File.separator)
|
val builder = ProcessBuilder("java", "-jar", "BuildTools.jar", "--rev", "latest", "-o", ".." + File.separator + ".." + File.separator)
|
||||||
builder.directory(File(directory + "ServerCraft" + File.separator + "Spigot"))
|
builder.directory(File(directory + "ServerCraft" + File.separator + "Spigot"))
|
||||||
val proc = builder.start()
|
val proc = builder.start()
|
||||||
val reader = InputStreamReader(proc.inputStream)
|
val reader = InputStreamReader(proc.inputStream)
|
||||||
|
val br = BufferedReader(reader)
|
||||||
try {
|
try {
|
||||||
var cbuf = CharArray(1000)
|
var line = br.readLine()
|
||||||
var currentline = 0.0
|
var currentline = 0.0
|
||||||
while (proc.isAlive) {
|
while (line != null) {
|
||||||
if (!building) {
|
if (!building) {
|
||||||
proc.destroy()
|
proc.destroy()
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))}
|
withContext(Dispatchers.JavaFx) {log(line)}
|
||||||
cbuf = CharArray(1000)
|
line = br.readLine()
|
||||||
reader.read(cbuf, 0, 1000)
|
|
||||||
currentline++
|
currentline++
|
||||||
if (currentline > 15) {
|
if (currentline > 15) {
|
||||||
withContext(Dispatchers.JavaFx) {progressBar.progress = if (spigotBuilt) {currentline/1100.0} else {currentline/14122.0} }
|
withContext(Dispatchers.JavaFx) {progressBar.progress = if (spigotBuilt) {currentline/1100.0} else {currentline/14122.0} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cbuf = CharArray(1000)
|
|
||||||
reader.read(cbuf, 0, 1000)
|
|
||||||
withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))}
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
withContext(Dispatchers.JavaFx) {log("Stream Closed\n")}
|
withContext(Dispatchers.JavaFx) {log("Stream Closed")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.isVisible = false
|
progressBar.isVisible = false
|
||||||
findServerJar()
|
|
||||||
withContext(Dispatchers.JavaFx){
|
withContext(Dispatchers.JavaFx){
|
||||||
worldSettingsPane.isDisable = false
|
worldSettingsPane.isDisable = false
|
||||||
directoryPane.isDisable = false
|
directoryPane.isDisable = false
|
||||||
|
@ -550,11 +538,11 @@ class PrimaryController {
|
||||||
@FXML
|
@FXML
|
||||||
private fun onStart() {
|
private fun onStart() {
|
||||||
if (started) {
|
if (started) {
|
||||||
killDialog.show()
|
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()) {
|
||||||
val res = EulaDialog().show()
|
val res = eulaDialog()
|
||||||
if (res) {
|
if (res) {
|
||||||
File(directory + "eula.txt").writeText("eula=true")
|
File(directory + "eula.txt").writeText("eula=true")
|
||||||
} else {
|
} else {
|
||||||
|
@ -571,34 +559,30 @@ 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("${directory}ServerCraft${File.separator}Java${File.separator}jdk-20.0.1${File.separator}bin${File.separator}java", "-Xmx${server.getProp("jvm-ram")}M", "-jar", "${server.jar}")
|
val builder = ProcessBuilder("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)
|
||||||
|
val br = BufferedReader(reader)
|
||||||
try {
|
try {
|
||||||
var cbuf = CharArray(1000)
|
var line = br.readLine()
|
||||||
reader.read(cbuf, 0, 1000)
|
while (line != null) {
|
||||||
while (proc.isAlive) {
|
if (asyncResult) {
|
||||||
if (killDialog.result) {
|
|
||||||
withContext(Dispatchers.JavaFx) {
|
withContext(Dispatchers.JavaFx) {
|
||||||
statusBar.text = "Killing Minecraft server..."
|
statusBar.text = "Killing Minecraft server..."
|
||||||
startButton.isDisable = true
|
startButton.isDisable = true
|
||||||
}
|
}
|
||||||
proc.destroy()
|
proc.destroy()
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))}
|
withContext(Dispatchers.JavaFx) {log(line)}
|
||||||
cbuf = CharArray(1000)
|
line = br.readLine()
|
||||||
reader.read(cbuf, 0, 1000)
|
|
||||||
}
|
}
|
||||||
cbuf = CharArray(1000)
|
|
||||||
reader.read(cbuf, 0, 1000)
|
|
||||||
withContext(Dispatchers.JavaFx) {log(cbuf.joinToString(separator=""))}
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
withContext(Dispatchers.JavaFx) {log("Stream Closed\n")}
|
withContext(Dispatchers.JavaFx) {log("Stream Closed")}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.JavaFx) {
|
withContext(Dispatchers.JavaFx) {
|
||||||
statusBar.text = if (killDialog.result) {
|
statusBar.text = if (asyncResult) {
|
||||||
killDialog.result = false
|
asyncResult = false
|
||||||
"Server killed."
|
"Server killed."
|
||||||
} else {
|
} else {
|
||||||
"Server stopped."
|
"Server stopped."
|
||||||
|
@ -615,6 +599,132 @@ class PrimaryController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun eulaDialog(): Boolean {
|
||||||
|
var result = false
|
||||||
|
val resources = App().javaClass.getResource("icons/warning.png")
|
||||||
|
val dialog = Stage()
|
||||||
|
dialog.icons.add(Image(App().javaClass.getResourceAsStream("app.png")))
|
||||||
|
dialog.setResizable(false)
|
||||||
|
dialog.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
dialog.title = directory
|
||||||
|
val scenePane = Pane()
|
||||||
|
val dialogScene = Scene(scenePane, 400.0, 150.0);
|
||||||
|
val imagePane = Pane()
|
||||||
|
val icon = Image("$resources")
|
||||||
|
imagePane.layoutX = 14.0
|
||||||
|
imagePane.layoutY = 14.0
|
||||||
|
imagePane.scaleX = 0.7
|
||||||
|
imagePane.scaleY = 0.7
|
||||||
|
imagePane.children.add(ImageView(icon))
|
||||||
|
val label = Label("Do you agree to the terms of the Minecraft End User License Agreement?")
|
||||||
|
label.isWrapText = true
|
||||||
|
label.layoutX = 115.0
|
||||||
|
label.layoutY = 40.0
|
||||||
|
val buttonBar = ButtonBar()
|
||||||
|
buttonBar.buttonOrder = "L+R"
|
||||||
|
buttonBar.padding = Insets(10.0, 10.0, 10.0, 10.0)
|
||||||
|
buttonBar.layoutX = 0.0
|
||||||
|
buttonBar.layoutY = 107.0
|
||||||
|
buttonBar.prefWidth = 400.0
|
||||||
|
val noButton = Button("I Disagree")
|
||||||
|
noButton.onAction = EventHandler<ActionEvent>() {
|
||||||
|
result = false
|
||||||
|
dialog.hide()
|
||||||
|
}
|
||||||
|
noButton.isDefaultButton = true
|
||||||
|
val yesButton = Button("I Agree")
|
||||||
|
yesButton.onAction = EventHandler<ActionEvent>() {
|
||||||
|
result = true
|
||||||
|
dialog.hide()
|
||||||
|
}
|
||||||
|
val eula = Button("View EULA")
|
||||||
|
eula.onAction = EventHandler<ActionEvent>() {
|
||||||
|
val desktop = Desktop.getDesktop()
|
||||||
|
if (desktop.isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
// most likely running on Windows or macOS
|
||||||
|
try {
|
||||||
|
desktop.browse(URI("https://account.mojang.com/documents/minecraft_eula"))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// assume running on linux
|
||||||
|
try {
|
||||||
|
Runtime.getRuntime().exec("xdg-open https://account.mojang.com/documents/minecraft_eula");
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ButtonBar.setButtonData(eula, ButtonBar.ButtonData.LEFT)
|
||||||
|
ButtonBar.setButtonData(noButton, ButtonBar.ButtonData.RIGHT)
|
||||||
|
ButtonBar.setButtonData(yesButton, ButtonBar.ButtonData.RIGHT)
|
||||||
|
buttonBar.buttons.add(eula)
|
||||||
|
buttonBar.buttons.add(noButton)
|
||||||
|
buttonBar.buttons.add(yesButton)
|
||||||
|
scenePane.children.addAll(imagePane, label, buttonBar)
|
||||||
|
dialog.setScene(dialogScene);
|
||||||
|
dialog.showAndWait();
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
dialog.icons.add(Image(App().javaClass.getResourceAsStream("app.png")))
|
||||||
|
dialog.setResizable(false)
|
||||||
|
dialog.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
dialog.title = directory
|
||||||
|
val scenePane = Pane()
|
||||||
|
val dialogScene = Scene(scenePane, 400.0, 150.0);
|
||||||
|
val imagePane = Pane()
|
||||||
|
val icon = Image("$resources")
|
||||||
|
imagePane.layoutX = 14.0
|
||||||
|
imagePane.layoutY = 14.0
|
||||||
|
imagePane.scaleX = 0.7
|
||||||
|
imagePane.scaleY = 0.7
|
||||||
|
imagePane.children.add(ImageView(icon))
|
||||||
|
val label = Label(msg)
|
||||||
|
label.isWrapText = true
|
||||||
|
label.layoutX = 115.0
|
||||||
|
label.layoutY = if (type == "warning") {10.0} else {40.0}
|
||||||
|
val buttonBar = ButtonBar()
|
||||||
|
buttonBar.padding = Insets(10.0, 10.0, 10.0, 10.0)
|
||||||
|
buttonBar.layoutX = 0.0
|
||||||
|
buttonBar.layoutY = 107.0
|
||||||
|
buttonBar.prefWidth = 400.0
|
||||||
|
val noButton = Button(no)
|
||||||
|
noButton.onAction = EventHandler<ActionEvent>() {
|
||||||
|
if (hold) {
|
||||||
|
result = false
|
||||||
|
} else {
|
||||||
|
asyncResult = false
|
||||||
|
}
|
||||||
|
dialog.hide()
|
||||||
|
}
|
||||||
|
val yesButton = Button(yes)
|
||||||
|
yesButton.onAction = EventHandler<ActionEvent>() {
|
||||||
|
if (hold) {
|
||||||
|
result = true
|
||||||
|
} else {
|
||||||
|
asyncResult = true
|
||||||
|
}
|
||||||
|
dialog.hide()
|
||||||
|
}
|
||||||
|
yesButton.isDefaultButton = true
|
||||||
|
buttonBar.buttons.add(noButton)
|
||||||
|
buttonBar.buttons.add(yesButton)
|
||||||
|
scenePane.children.addAll(imagePane, label, buttonBar)
|
||||||
|
dialog.setScene(dialogScene);
|
||||||
|
if (hold) {
|
||||||
|
dialog.showAndWait();
|
||||||
|
} else {
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadServerDir(dir: String): Boolean {
|
private fun loadServerDir(dir: String): Boolean {
|
||||||
directory = dir
|
directory = dir
|
||||||
// exit if doesn't exist for whatever reason
|
// exit if doesn't exist for whatever reason
|
||||||
|
@ -641,27 +751,23 @@ 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 = CommonDialog("warning", directory, "This server directory was not created by \nServerCraft. Errors may occur; copying\nthe world directories to a new folder may be\nsafer. Proceed anyway?").show()
|
val result = 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 = true
|
startButton.isDisable = false
|
||||||
File(directory + "ServerCraft").mkdir()
|
|
||||||
buildButton.text = "Build Server"
|
|
||||||
statusBar.text = "Server converted. Build the server to start."
|
|
||||||
server.loadProps(dir, convert=true)
|
|
||||||
}
|
}
|
||||||
|
server.loadProps(dir)
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
// assume clean directory
|
// assume clean directory
|
||||||
val result = CommonDialog("info", directory, "There is no server in this directory.\nCreate one?").show()
|
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"
|
||||||
server.dir = dir
|
|
||||||
server.loadProps()
|
|
||||||
}
|
}
|
||||||
statusBar.text = "Ready."
|
statusBar.text = "Ready."
|
||||||
|
server.loadProps()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package xyz.brysonsteck.ServerCraft.dialogs
|
|
||||||
|
|
||||||
import javafx.scene.control.Button
|
|
||||||
import javafx.event.EventHandler
|
|
||||||
import javafx.event.ActionEvent
|
|
||||||
|
|
||||||
class CommonDialog: Dialog {
|
|
||||||
override var type: String
|
|
||||||
override var title: String
|
|
||||||
override var msg: String
|
|
||||||
override val neutralButton: Button?
|
|
||||||
override val yesButton: Button?
|
|
||||||
override val noButton: Button?
|
|
||||||
|
|
||||||
constructor(type: String, title: String, msg: String) {
|
|
||||||
this.type = type
|
|
||||||
this.title = title
|
|
||||||
this.msg = msg
|
|
||||||
|
|
||||||
noButton = Button("No")
|
|
||||||
noButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = false
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
yesButton = Button("Yes")
|
|
||||||
yesButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = true
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
yesButton.isDefaultButton = true
|
|
||||||
neutralButton = null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package xyz.brysonsteck.ServerCraft.dialogs
|
|
||||||
|
|
||||||
import javafx.scene.control.Button
|
|
||||||
import javafx.scene.control.ButtonBar
|
|
||||||
import javafx.scene.control.Label
|
|
||||||
import javafx.scene.image.Image
|
|
||||||
import javafx.scene.image.ImageView
|
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import javafx.scene.Scene
|
|
||||||
import javafx.stage.Modality
|
|
||||||
import javafx.stage.Stage
|
|
||||||
import javafx.geometry.Insets
|
|
||||||
import javafx.event.EventHandler
|
|
||||||
import javafx.event.ActionEvent
|
|
||||||
import xyz.brysonsteck.ServerCraft.App
|
|
||||||
|
|
||||||
abstract class Dialog {
|
|
||||||
public var result = false
|
|
||||||
|
|
||||||
abstract val type: String
|
|
||||||
abstract val title: String
|
|
||||||
abstract val msg: String
|
|
||||||
abstract val neutralButton: Button?
|
|
||||||
abstract val yesButton: Button?
|
|
||||||
abstract val noButton: Button?
|
|
||||||
|
|
||||||
protected var hold: Boolean = true
|
|
||||||
protected var labelOffset: Double = 40.0
|
|
||||||
protected val dialog = Stage()
|
|
||||||
|
|
||||||
public fun show(): Boolean {
|
|
||||||
val resources = App().javaClass.getResource("icons/$type.png")
|
|
||||||
val icon = Image("$resources")
|
|
||||||
dialog.icons.add(Image(App().javaClass.getResourceAsStream("app.png")))
|
|
||||||
dialog.setResizable(false)
|
|
||||||
dialog.initModality(Modality.APPLICATION_MODAL);
|
|
||||||
dialog.title = title
|
|
||||||
val imagePane = Pane()
|
|
||||||
imagePane.layoutX = 14.0
|
|
||||||
imagePane.layoutY = 14.0
|
|
||||||
imagePane.scaleX = 0.7
|
|
||||||
imagePane.scaleY = 0.7
|
|
||||||
imagePane.children.add(ImageView(icon))
|
|
||||||
val label = Label(msg)
|
|
||||||
label.isWrapText = true
|
|
||||||
label.layoutX = 115.0
|
|
||||||
label.layoutY = labelOffset
|
|
||||||
val buttonBar = ButtonBar()
|
|
||||||
buttonBar.padding = Insets(10.0)
|
|
||||||
buttonBar.layoutX = 0.0
|
|
||||||
buttonBar.layoutY = 107.0
|
|
||||||
buttonBar.prefWidth = 400.0
|
|
||||||
val scenePane = Pane()
|
|
||||||
scenePane.children.addAll(imagePane, label, buttonBar)
|
|
||||||
if (noButton != null) {
|
|
||||||
ButtonBar.setButtonData(noButton, ButtonBar.ButtonData.RIGHT)
|
|
||||||
buttonBar.buttons.add(noButton)
|
|
||||||
}
|
|
||||||
if (yesButton != null) {
|
|
||||||
ButtonBar.setButtonData(yesButton, ButtonBar.ButtonData.RIGHT)
|
|
||||||
buttonBar.buttons.add(yesButton)
|
|
||||||
}
|
|
||||||
if (neutralButton != null) {
|
|
||||||
ButtonBar.setButtonData(neutralButton, ButtonBar.ButtonData.LEFT)
|
|
||||||
buttonBar.buttons.add(neutralButton)
|
|
||||||
}
|
|
||||||
val dialogScene = Scene(scenePane, 400.0, 150.0);
|
|
||||||
dialog.setScene(dialogScene);
|
|
||||||
if (hold) {
|
|
||||||
dialog.showAndWait();
|
|
||||||
} else {
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package xyz.brysonsteck.ServerCraft.dialogs
|
|
||||||
|
|
||||||
import xyz.brysonsteck.ServerCraft.dialogs.Dialog
|
|
||||||
import javafx.scene.control.Button
|
|
||||||
import javafx.event.EventHandler
|
|
||||||
import javafx.event.ActionEvent
|
|
||||||
import java.awt.Desktop
|
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
class EulaDialog: Dialog {
|
|
||||||
override val type: String
|
|
||||||
override val title: String
|
|
||||||
override val msg: String
|
|
||||||
override val neutralButton: Button?
|
|
||||||
override val yesButton: Button?
|
|
||||||
override val noButton: Button?
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
type = "warning"
|
|
||||||
title = "Minecraft End User License Agreement (EULA)"
|
|
||||||
msg = "Do you agree to the terms of the Minecraft End User License Agreement?"
|
|
||||||
|
|
||||||
noButton = Button("I Disagree")
|
|
||||||
noButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = false
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
noButton.isDefaultButton = true
|
|
||||||
yesButton = Button("I Agree")
|
|
||||||
yesButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = true
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
neutralButton = Button("View EULA")
|
|
||||||
neutralButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
val desktop = Desktop.getDesktop()
|
|
||||||
if (desktop.isSupported(Desktop.Action.BROWSE)) {
|
|
||||||
// most likely running on Windows or macOS
|
|
||||||
try {
|
|
||||||
desktop.browse(URI("https://account.mojang.com/documents/minecraft_eula"))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
println(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// assume running on linux
|
|
||||||
try {
|
|
||||||
Runtime.getRuntime().exec("xdg-open https://account.mojang.com/documents/minecraft_eula");
|
|
||||||
} catch (e: Exception) {
|
|
||||||
println(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package xyz.brysonsteck.ServerCraft.dialogs
|
|
||||||
|
|
||||||
import javafx.scene.control.Button
|
|
||||||
import javafx.event.EventHandler
|
|
||||||
import javafx.event.ActionEvent
|
|
||||||
|
|
||||||
class KillDialog: Dialog {
|
|
||||||
override val type: String
|
|
||||||
override val title: String
|
|
||||||
override val msg: String
|
|
||||||
override val neutralButton: Button?
|
|
||||||
override val yesButton: Button?
|
|
||||||
override val noButton: Button?
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
type = "warning"
|
|
||||||
title = "Server is still running!"
|
|
||||||
msg = "You should only kill the server if absolutely necessary, i.e. not being responsive after long periods of time. Data loss may occur. Continue anyway?"
|
|
||||||
hold = false
|
|
||||||
|
|
||||||
noButton = Button("No")
|
|
||||||
noButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = false
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
yesButton = Button("Yes")
|
|
||||||
yesButton.onAction = EventHandler<ActionEvent>() {
|
|
||||||
result = true
|
|
||||||
dialog.hide()
|
|
||||||
}
|
|
||||||
yesButton.isDefaultButton = true
|
|
||||||
neutralButton = null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -54,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package xyz.brysonsteck.ServerCraft.server
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
public class Server {
|
public class Server {
|
||||||
|
@ -34,39 +33,16 @@ public class Server {
|
||||||
props.setProperty("level-seed", "")
|
props.setProperty("level-seed", "")
|
||||||
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")
|
||||||
writeProps()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun loadProps(dir: String, convert: Boolean = false) {
|
public fun loadProps(dir: String) {
|
||||||
var ins = File(dir + File.separator + "server.properties").inputStream()
|
val ins = File(dir + File.separator + "server.properties").inputStream()
|
||||||
props.load(ins)
|
props.load(ins)
|
||||||
try {
|
|
||||||
ins = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").inputStream()
|
|
||||||
props.load(ins)
|
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
if (convert) {
|
|
||||||
// create the file in question, as this is an external server being converted into a ServerCraft managed one
|
|
||||||
File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").createNewFile()
|
|
||||||
// also apply app-specific properties
|
|
||||||
props.setProperty("jvm-ram", 1024.toString())
|
|
||||||
// then write to file
|
|
||||||
val temp = Properties()
|
|
||||||
val outs = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").outputStream()
|
|
||||||
temp.setProperty("jvm-ram", props.getProperty("jvm-ram"))
|
|
||||||
temp.store(outs, "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft")
|
|
||||||
} else {
|
|
||||||
println(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeProps() {
|
private fun writeProps() {
|
||||||
var outs = File(dir + File.separator + "server.properties").outputStream()
|
val outs = File(dir + File.separator + "server.properties").outputStream()
|
||||||
props.store(outs, "Minecraft server properties\nCreated with ServerCraft: https://codeberg.org/brysonsteck/ServerCraft")
|
props.store(outs, "Minecraft server properties\nCreated with ServerCraft: https://codeberg.org/brysonsteck/ServerCraft")
|
||||||
val temp = Properties()
|
|
||||||
outs = File(dir + File.separator + "ServerCraft" + File.separator + "ServerCraft.properties").outputStream()
|
|
||||||
temp.setProperty("jvm-ram", props.getProperty("jvm-ram"))
|
|
||||||
temp.store(outs, "ServerCraft settings backup\nSometimes, Minecraft will completely overwrite the server.properties file,\ncompletely destroying these app-specific settings. This file backs up these settings\njust in case. :)\nhttps://codeberg.org/brysonsteck/ServerCraft")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun getProp(prop: String): String {
|
public fun getProp(prop: String): String {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<Font name="System Bold" size="28.0" />
|
<Font name="System Bold" size="28.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Label fx:id="version" layoutX="146.0" layoutY="82.0" text="Version " />
|
<Label layoutX="146.0" layoutY="82.0" text="Version 1.0" />
|
||||||
<ButtonBar layoutY="318.0" prefHeight="40.0" prefWidth="398.0">
|
<ButtonBar layoutY="318.0" prefHeight="40.0" prefWidth="398.0">
|
||||||
<buttons>
|
<buttons>
|
||||||
<Button mnemonicParsing="false" onAction="#closeInfo" text="Close" />
|
<Button mnemonicParsing="false" onAction="#closeInfo" text="Close" />
|
||||||
|
@ -36,24 +36,20 @@
|
||||||
<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>
|
||||||
<Label alignment="CENTER" layoutX="1.0" layoutY="135.0" prefHeight="15.0" prefWidth="398.0" text="A graphical interface for creating Minecraft servers" textAlignment="CENTER" />
|
<Label layoutX="52.0" layoutY="135.0" text="A graphical interface for creating Minecraft servers" textAlignment="CENTER" />
|
||||||
<TabPane layoutY="158.0" prefHeight="166.0" prefWidth="398.0" stylesheets="@css/info-tabs.css" tabClosingPolicy="UNAVAILABLE">
|
<TabPane layoutY="158.0" prefHeight="161.0" prefWidth="398.0" stylesheets="@css/info-tabs.css" tabClosingPolicy="UNAVAILABLE">
|
||||||
<tabs>
|
<tabs>
|
||||||
<Tab text="About">
|
<Tab text="About">
|
||||||
<content>
|
<content>
|
||||||
<Pane prefHeight="200.0" prefWidth="398.0">
|
<Pane prefHeight="200.0" prefWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
<Label alignment="TOP_LEFT" prefWidth="398.0" text="This program is for simple Minecraft servers, and expects that the user knows how to port forward. ServerCraft is free and open source under the GNU General Public License Version 3.0" wrapText="true">
|
<Label alignment="TOP_LEFT" prefWidth="398.0" text="This program is for simple Minecraft servers, and expects that the user knows how to port forward. ServerCraft is free and open source under the GNU General Public License Version 3.0" wrapText="true">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="13.0" left="13.0" right="13.0" top="13.0" />
|
<Insets bottom="13.0" left="13.0" right="13.0" top="13.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</Label>
|
</Label>
|
||||||
<HBox alignment="CENTER" layoutY="106.0" prefHeight="21.0" prefWidth="398.0">
|
<Hyperlink fx:id="license" layoutX="127.0" layoutY="110.0" onAction="#openHyperlink" text="License" />
|
||||||
<children>
|
<Hyperlink fx:id="source" layoutX="189.0" layoutY="110.0" onAction="#openHyperlink" text="Source Code" />
|
||||||
<Hyperlink fx:id="license" onAction="#openHyperlink" text="License" />
|
|
||||||
<Hyperlink fx:id="source" onAction="#openHyperlink" text="Source Code" />
|
|
||||||
</children>
|
|
||||||
</HBox>
|
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="13.0" left="13.0" right="13.0" top="13.0" />
|
<Insets bottom="13.0" left="13.0" right="13.0" top="13.0" />
|
||||||
|
@ -65,7 +61,7 @@
|
||||||
<content>
|
<content>
|
||||||
<Pane prefHeight="200.0" prefWidth="200.0">
|
<Pane prefHeight="200.0" prefWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
<Label alignment="CENTER" layoutY="109.0" prefHeight="15.0" prefWidth="398.0" text="Want to join the list? Contribute with a PR!" textAlignment="CENTER" />
|
<Label layoutX="78.0" layoutY="109.0" text="Want to join the list? Contribute with a PR!" textAlignment="CENTER" />
|
||||||
<VBox>
|
<VBox>
|
||||||
<children>
|
<children>
|
||||||
<Label text="Bryson Steck" VBox.vgrow="ALWAYS">
|
<Label text="Bryson Steck" VBox.vgrow="ALWAYS">
|
||||||
|
|
Loading…
Add table
Reference in a new issue