aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--app/bin/main/xyz/brysonsteck/serverfordummies/App.kt30
-rw-r--r--app/bin/main/xyz/brysonsteck/serverfordummies/Main.kt5
-rw-r--r--app/bin/main/xyz/brysonsteck/serverfordummies/PrimaryController.kt124
-rw-r--r--app/bin/main/xyz/brysonsteck/serverfordummies/downloadFile.kt37
-rw-r--r--app/bin/main/xyz/brysonsteck/serverfordummies/primary.fxml253
-rw-r--r--app/bin/test/xyz/brysonsteck/serverfordummies/AppTest.kt14
-rw-r--r--app/build.gradle11
-rw-r--r--app/src/main/kotlin/xyz/brysonsteck/serverfordummies/App.kt2
-rw-r--r--app/src/main/kotlin/xyz/brysonsteck/serverfordummies/Download.kt119
-rw-r--r--app/src/main/kotlin/xyz/brysonsteck/serverfordummies/PrimaryController.kt95
-rw-r--r--app/src/main/kotlin/xyz/brysonsteck/serverfordummies/downloadFile.kt37
-rw-r--r--app/src/main/kotlin/xyz/brysonsteck/serverfordummies/server/Server.kt7
-rw-r--r--app/src/main/resources/xyz/brysonsteck/serverfordummies/dialog.fxml24
-rw-r--r--app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/info.pngbin0 -> 5289 bytes
-rw-r--r--app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/warning.pngbin0 -> 5131 bytes
-rw-r--r--app/src/main/resources/xyz/brysonsteck/serverfordummies/primary.fxml6
17 files changed, 254 insertions, 511 deletions
diff --git a/.gitignore b/.gitignore
index a0672f7..56cc083 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
# Ignore Gradle build output directory
build
+bin
# Ignore VSCode settings
.vscode
diff --git a/app/bin/main/xyz/brysonsteck/serverfordummies/App.kt b/app/bin/main/xyz/brysonsteck/serverfordummies/App.kt
deleted file mode 100644
index 0547a8f..0000000
--- a/app/bin/main/xyz/brysonsteck/serverfordummies/App.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This Kotlin source file was generated by the Gradle 'init' task.
- */
-package xyz.brysonsteck.serverfordummies
-
-import javafx.application.Application;
-import javafx.fxml.FXMLLoader;
-import javafx.scene.Parent;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-
-class App : Application() {
-
- override fun start(stage: Stage) {
- var scene = Scene(loadFXML("primary"), 1500.0, 900.0)
- stage.title = "Server For Dummies"
- stage.scene = scene
- stage.show()
- }
-
- private fun loadFXML(fxml: String) : Parent {
- val fxmlLoader = FXMLLoader(this.javaClass.getResource(fxml + ".fxml"))
- return fxmlLoader.load()
- }
-
- public fun run() {
- launch()
- }
-
-}
diff --git a/app/bin/main/xyz/brysonsteck/serverfordummies/Main.kt b/app/bin/main/xyz/brysonsteck/serverfordummies/Main.kt
deleted file mode 100644
index 604825f..0000000
--- a/app/bin/main/xyz/brysonsteck/serverfordummies/Main.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package xyz.brysonsteck.serverfordummies
-
-fun main() {
- App().run()
-} \ No newline at end of file
diff --git a/app/bin/main/xyz/brysonsteck/serverfordummies/PrimaryController.kt b/app/bin/main/xyz/brysonsteck/serverfordummies/PrimaryController.kt
deleted file mode 100644
index 6e05579..0000000
--- a/app/bin/main/xyz/brysonsteck/serverfordummies/PrimaryController.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-package xyz.brysonsteck.serverfordummies
-
-import java.io.File
-import java.io.IOException
-import java.awt.Checkbox
-import java.util.Properties
-
-import javafx.beans.value.ChangeListener
-import javafx.beans.value.ObservableValue
-import javafx.beans.property.BooleanProperty
-import javafx.collections.FXCollections
-import javafx.fxml.FXML
-import javafx.geometry.Insets
-import javafx.scene.control.Button
-import javafx.scene.control.ChoiceBox
-import javafx.scene.control.Label
-import javafx.scene.control.TextField
-import javafx.scene.control.Spinner
-import javafx.scene.control.TitledPane
-import javafx.scene.control.ButtonBar
-import javafx.scene.control.CheckBox
-import javafx.scene.layout.Border
-import javafx.scene.layout.BorderStroke
-import javafx.scene.layout.GridPane
-import javafx.scene.layout.Pane
-import javafx.scene.layout.HBox
-import javafx.scene.text.TextAlignment
-import javafx.scene.Scene
-import javafx.scene.input.MouseEvent
-import javafx.stage.FileChooser
-import javafx.stage.FileChooser.ExtensionFilter
-import javafx.stage.DirectoryChooser
-import javafx.event.EventHandler
-
-class PrimaryController {
- @FXML
- lateinit private var currentDirectoryLabel: Label
- @FXML
- lateinit private var worldNameField: TextField
- @FXML
- lateinit private var seedField: TextField
- @FXML
- lateinit private var portSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var difficultyBox: ChoiceBox<String>
- @FXML
- lateinit private var gamemodeBox: ChoiceBox<String>
- @FXML
- lateinit private var worldTypeBox: ChoiceBox<String>
- @FXML
- lateinit private var worldSettingsPane: HBox
- @FXML
- lateinit private var parentPane: Pane
- @FXML
- lateinit private var buttonBar: ButtonBar
- @FXML
- lateinit private var flightCheckbox: CheckBox
- @FXML
- lateinit private var netherCheckbox: CheckBox
- @FXML
- lateinit private var structuresCheckbox: CheckBox
- @FXML
- lateinit private var pvpCheckbox: CheckBox
- @FXML
- lateinit private var whitelistCheckbox: CheckBox
- @FXML
- lateinit private var cmdBlocksCheckbox: CheckBox
- @FXML
- lateinit private var playerCountCheckbox: CheckBox
- @FXML
- lateinit private var maxPlayersSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var maxSizeSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var memorySpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var spawnSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var simulationSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var renderSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var maxTickSpinner: Spinner<kotlin.Int>
- @FXML
- lateinit private var progressBar: ProgressBar
-
- @FXML
- private fun onDirectoryButtonClick() {
- val dirChooser = DirectoryChooser()
- dirChooser.title = "Open a server directory"
- dirChooser.initialDirectory = File(System.getProperty("user.home"))
- val result = dirChooser.showDialog(null)
- if (result != null) {
- currentDirectoryLabel.text = result.absolutePath
- parentPane.isDisable = false
- worldSettingsPane.isDisable = false
- buttonBar.isDisable = false
- }
- }
-
- @FXML
- private fun onWorldNameChange() {
-
- }
-
- @FXML
- private fun onSeedChange() {
-
- }
-
- @FXML
- private fun onPortChange() {
-
- }
-
- @FXML
- private fun onCheckboxClick() {
- }
-
- @FXML
- private fun onSpinnerChange() {
-
- }
-} \ No newline at end of file
diff --git a/app/bin/main/xyz/brysonsteck/serverfordummies/downloadFile.kt b/app/bin/main/xyz/brysonsteck/serverfordummies/downloadFile.kt
deleted file mode 100644
index bbaeab5..0000000
--- a/app/bin/main/xyz/brysonsteck/serverfordummies/downloadFile.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-import java.net.http.HttpClient
-import kotlinx.coroutines.flow
-
-sealed class DownloadStatus {
-
- object Success : DownloadStatus()
-
- data class Error(val message: String) : DownloadStatus()
-
- data class Progress(val progress: Int): DownloadStatus()
-
-}
-
-// function from https://gist.githubusercontent.com/SG-K/63e379efcc3d1cd3ce4fb56ee0e29c42/raw/cd9a4a016401b7c54ec01303415b5871ffa26066/downloadFile.kt
-suspend fun HttpClient.downloadFile(file: File, url: String): Flow<DownloadStatus> {
- return flow {
- val response = call {
- url(url)
- method = HttpMethod.Get
- }.response
- val byteArray = ByteArray(response.contentLength()!!.toInt())
- var offset = 0
- do {
- val currentRead = response.content.readAvailable(byteArray, offset, byteArray.size)
- offset += currentRead
- val progress = (offset * 100f / byteArray.size).roundToInt()
- emit(DownloadStatus.Progress(progress))
- } while (currentRead > 0)
- response.close()
- if (response.status.isSuccess()) {
- file.writeBytes(byteArray)
- emit(DownloadStatus.Success)
- } else {
- emit(DownloadStatus.Error("File not downloaded"))
- }
- }
-} \ No newline at end of file
diff --git a/app/bin/main/xyz/brysonsteck/serverfordummies/primary.fxml b/app/bin/main/xyz/brysonsteck/serverfordummies/primary.fxml
deleted file mode 100644
index 85b7370..0000000
--- a/app/bin/main/xyz/brysonsteck/serverfordummies/primary.fxml
+++ /dev/null
@@ -1,253 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.geometry.Insets?>
-<?import javafx.scene.control.Button?>
-<?import javafx.scene.control.ButtonBar?>
-<?import javafx.scene.control.CheckBox?>
-<?import javafx.scene.control.ChoiceBox?>
-<?import javafx.scene.control.ContextMenu?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.MenuItem?>
-<?import javafx.scene.control.ProgressBar?>
-<?import javafx.scene.control.Separator?>
-<?import javafx.scene.control.Spinner?>
-<?import javafx.scene.control.TextField?>
-<?import javafx.scene.control.TitledPane?>
-<?import javafx.scene.control.Tooltip?>
-<?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="900.0" maxWidth="1500.0" minHeight="620.0" minWidth="963.0" prefHeight="708.0" prefWidth="963.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="xyz.brysonsteck.serverfordummies.PrimaryController">
- <children>
- <HBox prefHeight="39.0" prefWidth="963.0">
- <children>
- <Button id="openFile" fx:id="chooseDirectoryButton" lineSpacing="10.0" mnemonicParsing="false" onMouseClicked="#onDirectoryButtonClick" text="Choose Directory...">
- <opaqueInsets>
- <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
- </opaqueInsets>
- <HBox.margin>
- <Insets />
- </HBox.margin>
- </Button>
- <Separator orientation="VERTICAL" prefHeight="200.0">
- <HBox.margin>
- <Insets left="5.0" />
- </HBox.margin>
- </Separator>
- <Label text="Server Directory:">
- <HBox.margin>
- <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
- </HBox.margin>
- <font>
- <Font name="System Bold" size="13.0" />
- </font>
- </Label>
- <Label id="currentFilename" fx:id="currentDirectoryLabel" text="&lt;NONE&gt;">
- <HBox.margin>
- <Insets bottom="5.0" right="5.0" top="5.0" />
- </HBox.margin>
- </Label>
- </children>
- <padding>
- <Insets bottom="7.0" left="7.0" right="7.0" top="7.0" />
- </padding>
- </HBox>
- <HBox fx:id="worldSettingsPane" disable="true" layoutY="39.0" prefHeight="41.0" prefWidth="963.0">
- <padding>
- <Insets bottom="7.0" left="7.0" right="7.0" top="7.0" />
- </padding>
- <children>
- <Label text="World Name:">
- <font>
- <Font name="System Bold" size="13.0" />
- </font>
- <HBox.margin>
- <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
- </HBox.margin>
- </Label>
- <TextField fx:id="worldNameField" onInputMethodTextChanged="#onWorldNameChange">
- <HBox.margin>
- <Insets top="2.0" />
- </HBox.margin>
- </TextField>
- <Separator orientation="VERTICAL" prefHeight="200.0">
- <HBox.margin>
- <Insets left="5.0" />
- </HBox.margin>
- </Separator>
- <Label text="Seed:">
- <font>
- <Font name="System Bold" size="13.0" />
- </font>
- <HBox.margin>
- <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
- </HBox.margin>
- </Label>
- <TextField fx:id="seedField" onInputMethodTextChanged="#onSeedChange" prefHeight="23.0" prefWidth="448.0">
- <HBox.margin>
- <Insets top="2.0" />
- </HBox.margin>
- </TextField>
- <Separator orientation="VERTICAL" prefHeight="200.0">
- <HBox.margin>
- <Insets left="5.0" />
- </HBox.margin>
- </Separator>
- <Label text="Server Port:">
- <font>
- <Font name="System Bold" size="13.0" />
- </font>
- <HBox.margin>
- <Insets bottom="5.0" left="5.0" right="5.0" top="6.0" />
- </HBox.margin>
- </Label>
- <Spinner fx:id="portSpinner" onInputMethodTextChanged="#onPortChange" prefHeight="23.0" prefWidth="112.0">
- <HBox.margin>
- <Insets top="2.0" />
- </HBox.margin>
- </Spinner>
- </children>
- <opaqueInsets>
- <Insets bottom="3.0" />
- </opaqueInsets>
- </HBox>
- <Pane fx:id="parentPane" disable="true" layoutY="78.0" prefHeight="555.0" prefWidth="970.0">
- <children>
- <TitledPane fx:id="settingsPane" animated="false" collapsible="false" layoutX="10.0" layoutY="7.0" prefHeight="273.0" prefWidth="627.0" text="Server Settings">
- <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" text="Allow The Nether" />
- <CheckBox fx:id="structuresCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="70.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" prefHeight="45.0" prefWidth="231.0" text="Generate Structures&#10;(such as villages and strongholds)" />
- <CheckBox fx:id="pvpCheckbox" layoutX="14.0" layoutY="109.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" text="Allow PvP" />
- <CheckBox fx:id="whitelistCheckbox" alignment="TOP_LEFT" layoutX="14.0" layoutY="138.0" mnemonicParsing="false" onMouseClicked="#onCheckboxClick" prefHeight="45.0" prefWidth="231.0" text="Enable Whitelist&#10;(Only users you specify can join)" />
- <Spinner fx:id="maxPlayerSpinner" layoutX="130.0" layoutY="179.0" prefHeight="23.0" prefWidth="99.0" />
- <Label layoutX="14.0" layoutY="183.0" text="Maximum Players:" />
- <Spinner fx:id="maxSizeSpinner" layoutX="214.0" layoutY="212.0" prefHeight="23.0" prefWidth="155.0" />
- <Label layoutX="14.0" layoutY="216.0" text="Maximum World Size (in blocks):" />
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- <TitledPane fx:id="advancedPane" animated="false" layoutX="10.0" layoutY="289.0" prefHeight="259.0" prefWidth="627.0" text="Advanced Server Settings">
- <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" />
- <Spinner fx:id="memorySpinner" layoutX="154.0" layoutY="69.0" prefHeight="23.0" prefWidth="99.0" />
- <Label ellipsisString="" layoutX="14.0" layoutY="73.0" text="Server Memory in MB:" textOverrun="CLIP">
- <tooltip>
- <Tooltip text="This is the amount of RAM that will get passed to Minecraft/the JVM.&#10;For simple servers, 1024 MB will be plenty.&#10;If you typically have more than 5 concurrent players, consider allocating more." />
- </tooltip>
- </Label>
- <Spinner fx:id="spawnSpinner" layoutX="172.0" layoutY="100.0" prefHeight="23.0" prefWidth="99.0" />
- <Label layoutX="14.0" layoutY="104.0" text="Spawn Protection Radius:">
- <tooltip>
- <Tooltip text="All blocks in a radius from 0,~,0 will be unbreakable. &#10;If you want to break blocks within spawn, change this value." />
- </tooltip>
- </Label>
- <Spinner fx:id="simulationSpinner" layoutX="147.0" layoutY="132.0" prefHeight="23.0" prefWidth="99.0" />
- <Label layoutX="14.0" layoutY="136.0" text="Simulation Distance:">
- <tooltip>
- <Tooltip text="The radius of chunks for each player where ticks will be updated.&#10;In other words, anything outside these circles, such as furnaces, mobs, etc, will not be updated or simulated." />
- </tooltip>
- </Label>
- <Spinner fx:id="renderSpinner" layoutX="124.0" layoutY="165.0" prefHeight="23.0" prefWidth="99.0" />
- <Label layoutX="14.0" layoutY="169.0" text="Render Distance:">
- <tooltip>
- <Tooltip text="The radius of chunks where the server will render the view distance.&#10;Any value higher on a client than what is set will be ignored.&#10;Higher values will be more demanding on the server." />
- </tooltip>
- </Label>
- <Label layoutX="14.0" layoutY="203.0" text="Maximum Tick Time (in milliseconds):">
- <tooltip>
- <Tooltip text="If the server cannot update ticks (i.e. &quot;lags&quot;) for longer than this amount of time, the server will shutdown.&#10;60000 ms (60 seconds) is the default." />
- </tooltip>
- </Label>
- <Spinner fx:id="maxTickSpinner" layoutX="246.0" layoutY="199.0" prefHeight="23.0" prefWidth="99.0" />
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- <TitledPane fx:id="difficultyPane" animated="false" collapsible="false" layoutX="649.0" layoutY="7.0" prefHeight="77.0" prefWidth="305.0" text="Difficulty">
- <content>
- <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="94.0" prefWidth="303.0">
- <children>
- <ChoiceBox fx:id="difficultyBox" layoutX="14.0" layoutY="14.0" prefHeight="23.0" prefWidth="276.0">
- <contextMenu>
- <ContextMenu>
- <items>
- <MenuItem mnemonicParsing="false" />
- </items>
- </ContextMenu>
- </contextMenu>
- </ChoiceBox>
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- <TitledPane fx:id="gamemodePane" animated="false" collapsible="false" layoutX="649.0" layoutY="92.0" prefHeight="77.0" prefWidth="305.0" text="Gamemode">
- <content>
- <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="94.0" prefWidth="303.0">
- <children>
- <ChoiceBox fx:id="gamemodeBox" layoutX="14.0" layoutY="14.0" prefHeight="23.0" prefWidth="276.0" />
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- <TitledPane fx:id="worldTypePane" animated="false" collapsible="false" layoutX="649.0" layoutY="178.0" prefHeight="77.0" prefWidth="305.0" text="World Type">
- <content>
- <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="94.0" prefWidth="303.0">
- <children>
- <ChoiceBox fx:id="worldTypeBox" layoutX="14.0" layoutY="14.0" prefHeight="23.0" prefWidth="276.0" />
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- <TitledPane fx:id="worldTypePane1" animated="false" collapsible="false" layoutX="649.0" layoutY="265.0" prefHeight="283.0" prefWidth="305.0" text="Whitelist">
- <content>
- <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="94.0" prefWidth="303.0">
- <children>
- <ChoiceBox layoutX="14.0" layoutY="14.0" prefHeight="23.0" prefWidth="276.0" />
- </children>
- </AnchorPane>
- </content>
- </TitledPane>
- </children>
- </Pane>
- <ButtonBar fx:id="buttonBar" disable="true" layoutY="635.0" prefHeight="40.0" prefWidth="963.0">
- <buttons>
- <Button mnemonicParsing="false" text="Build Server" />
- <Button defaultButton="true" mnemonicParsing="false" text="Start Server" />
- </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;">
- <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" progress="0.0" visible="false">
- <HBox.margin>
- <Insets left="460.0" />
- </HBox.margin>
- </ProgressBar>
- </children>
- <padding>
- <Insets bottom="9.0" left="9.0" right="9.0" top="9.0" />
- </padding>
- </HBox>
- </children>
-</Pane>
diff --git a/app/bin/test/xyz/brysonsteck/serverfordummies/AppTest.kt b/app/bin/test/xyz/brysonsteck/serverfordummies/AppTest.kt
deleted file mode 100644
index e1d1866..0000000
--- a/app/bin/test/xyz/brysonsteck/serverfordummies/AppTest.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * This Kotlin source file was generated by the Gradle 'init' task.
- */
-package xyz.brysonsteck.serverfordummies
-
-import kotlin.test.Test
-import kotlin.test.assertNotNull
-
-class AppTest {
- // @Test fun appHasAGreeting() {
- // val classUnderTest = App()
- // assertNotNull(classUnderTest.greeting, "app should have a greeting")
- // }
-}
diff --git a/app/build.gradle b/app/build.gradle
index 8add66f..ac1dd3b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,7 +8,7 @@
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
- id 'org.jetbrains.kotlin.jvm' version '1.6.0'
+ id 'org.jetbrains.kotlin.jvm' version '1.8.0'
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
@@ -32,12 +32,9 @@ dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:31.1-jre'
- // Ktor
- implementation "io.ktor:ktor-server-core:2.3.0"
- implementation "io.ktor:ktor-server-netty:2.3.0"
-
// Coroutines core
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.7.0"
}
application {
@@ -63,4 +60,4 @@ tasks.named('test') {
javafx {
version = "20"
modules = ['javafx.controls', 'javafx.fxml', 'javafx.graphics']
-} \ No newline at end of file
+}
diff --git a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/App.kt b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/App.kt
index 0547a8f..cffa525 100644
--- a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/App.kt
+++ b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/App.kt
@@ -18,7 +18,7 @@ class App : Application() {
stage.show()
}
- private fun loadFXML(fxml: String) : Parent {
+ public fun loadFXML(fxml: String) : Parent {
val fxmlLoader = FXMLLoader(this.javaClass.getResource(fxml + ".fxml"))
return fxmlLoader.load()
}
diff --git a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/Download.kt b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/Download.kt
new file mode 100644
index 0000000..da61f75
--- /dev/null
+++ b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/Download.kt
@@ -0,0 +1,119 @@
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+class Download: Runnable {
+ public enum class Status {
+ DOWNLOADING, PAUSED, COMPLETE, CANCELLED, ERROR
+ }
+ public var size: Int
+ public var downloaded: Int
+ public var contentLength: Int
+
+ private final val MAX_BUFFER_SIZE: Number = 1024
+
+ private var url: URL
+ private var status: Status
+
+ constructor (url: URL) {
+ this.url = url
+ size = -1
+ downloaded = 0
+ status = Status.DOWNLOADING
+ contentLength = 1
+ }
+
+ public fun status(): Status {
+ return status
+ }
+
+ public fun start() {
+ val thread = Thread(this)
+ thread.start()
+ }
+
+ private fun getFilename(url: URL): String {
+ val filename = url.getFile()
+ return filename.substring(filename.lastIndexOf('/') + 1)
+ }
+
+ override fun run() {
+ var stream: InputStream? = null
+ var file: RandomAccessFile? = null
+
+ try {
+ // Open connection to URL.
+ var connection = url.openConnection() as HttpURLConnection;
+
+ // Specify what portion of file to download.
+ connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
+
+ // Connect to server.
+ connection.connect();
+
+ // Make sure response code is in the 200 range.
+ if (connection.responseCode / 100 != 2) {
+ status = Status.ERROR
+ }
+
+ // Check for valid content length.
+ contentLength = connection.getContentLength();
+ if (contentLength < 1) {
+ status = Status.ERROR
+ }
+
+ /* Set the size for this download if it
+ hasn't been already set. */
+ if (size == -1) {
+ size = contentLength;
+ }
+
+ // Open file and seek to the end of it.
+ file = RandomAccessFile(getFilename(url), "rw");
+ file.seek(downloaded.toLong());
+
+ stream = connection.getInputStream();
+ while (status == Status.DOWNLOADING) {
+ /* Size buffer according to how much of the
+ file is left to download. */
+ val buffer: ByteArray;
+ if (size - downloaded > MAX_BUFFER_SIZE as Int) {
+ buffer = ByteArray(MAX_BUFFER_SIZE)
+ } else {
+ buffer = ByteArray(size - downloaded);
+ }
+
+ // Read from server into buffer.
+ val read = stream.read(buffer);
+ if (read == -1)
+ break;
+
+ // Write buffer to file.
+ file.write(buffer, 0, read);
+ downloaded += read;
+ }
+
+ /* Change status to complete if this point was
+ reached because downloading has finished. */
+ if (status == Status.DOWNLOADING) {
+ status = Status.COMPLETE;
+ }
+ } catch (e: Exception) {
+ status = Status.ERROR
+ } finally {
+ // Close file.
+ if (file != null) {
+ try {
+ file.close();
+ } catch (e: Exception) {}
+ }
+
+ // Close connection to server.
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (e: Exception) {}
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/PrimaryController.kt b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/PrimaryController.kt
index 6e05579..ac45e2a 100644
--- a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/PrimaryController.kt
+++ b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/PrimaryController.kt
@@ -1,12 +1,17 @@
package xyz.brysonsteck.serverfordummies
+import kotlinx.coroutines.*
+import kotlinx.coroutines.javafx.JavaFx
+
import java.io.File
import java.io.IOException
import java.awt.Checkbox
import java.util.Properties
+import java.net.URL
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
+import javafx.concurrent.Task
import javafx.beans.property.BooleanProperty
import javafx.collections.FXCollections
import javafx.fxml.FXML
@@ -19,18 +24,24 @@ import javafx.scene.control.Spinner
import javafx.scene.control.TitledPane
import javafx.scene.control.ButtonBar
import javafx.scene.control.CheckBox
+import javafx.scene.control.ProgressBar
import javafx.scene.layout.Border
import javafx.scene.layout.BorderStroke
import javafx.scene.layout.GridPane
import javafx.scene.layout.Pane
import javafx.scene.layout.HBox
+import javafx.scene.layout.VBox
import javafx.scene.text.TextAlignment
+import javafx.scene.text.Text
import javafx.scene.Scene
import javafx.scene.input.MouseEvent
import javafx.stage.FileChooser
import javafx.stage.FileChooser.ExtensionFilter
import javafx.stage.DirectoryChooser
+import javafx.stage.Modality
+import javafx.stage.Stage
import javafx.event.EventHandler
+import Download
class PrimaryController {
@FXML
@@ -82,6 +93,8 @@ class PrimaryController {
@FXML
lateinit private var maxTickSpinner: Spinner<kotlin.Int>
@FXML
+ lateinit private var statusBar: Label
+ @FXML
lateinit private var progressBar: ProgressBar
@FXML
@@ -92,6 +105,7 @@ class PrimaryController {
val result = dirChooser.showDialog(null)
if (result != null) {
currentDirectoryLabel.text = result.absolutePath
+ val real = loadServerDir(result.absolutePath)
parentPane.isDisable = false
worldSettingsPane.isDisable = false
buttonBar.isDisable = false
@@ -115,10 +129,91 @@ class PrimaryController {
@FXML
private fun onCheckboxClick() {
+
}
@FXML
private fun onSpinnerChange() {
}
+
+ @FXML
+ private fun onBuild() {
+ GlobalScope.launch(Dispatchers.Default) {
+ withContext(Dispatchers.JavaFx){statusBar.text = "Downloading a file..."}
+ progressBar.isVisible = true
+ val download = Download(URL("https://brysonsteck.xyz/pub/a-really-big-file"))
+ 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 work
+ print("")
+ if (prog >= 0.01) {
+ withContext(Dispatchers.JavaFx) {progressBar.progress = prog}
+ }
+ Thread.sleep(300)
+ }
+ progressBar.isVisible = false
+ withContext(Dispatchers.JavaFx){statusBar.text = "Ready."}
+ }
+ }
+
+ @FXML
+ private fun onStart() {
+
+ }
+
+ private fun createDialog() {
+ val dialog = Stage();
+ dialog.initModality(Modality.APPLICATION_MODAL);
+ val dialogScene = Scene(App().loadFXML("dialog"), 300.0, 200.0);
+ dialog.setScene(dialogScene);
+ dialog.show();
+ }
+
+ private fun loadServerDir(dir: String): Boolean {
+ createDialog()
+ var directory = dir
+ var hasServer = false
+ if (!File(directory).isDirectory) {
+ return false;
+ }
+
+ if (directory[directory.length-1] != File.separatorChar)
+ directory += File.separatorChar
+
+ val hasDummy = File(directory + "ServerForDummies").isDirectory
+
+ for (i in 20 downTo 8) {
+ for (j in 15 downTo 0) {
+ var spigotFile: String = ""
+ if (j == 0)
+ spigotFile += "spigot-1.$i.jar"
+ else
+ spigotFile += "spigot-1.$i.$j.jar";
+
+ hasServer = File(directory + spigotFile).isFile || File(directory + "server.jar").isFile
+ if (hasServer)
+ break;
+ }
+ }
+
+ val hasProperties = File(directory + File.separator + "server.properties").isFile
+
+ if (hasDummy && hasServer) {
+ // read jproperties
+ println("read jproperties")
+ } else if (hasDummy && !hasServer && hasProperties) {
+ // just needs to be built
+ println("build")
+ } else if (!hasDummy && hasServer) {
+ // server created externally
+ println("server made externally")
+ } else {
+ // assume clean directory
+ println("none")
+ }
+
+ return true;
+ }
} \ No newline at end of file
diff --git a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/downloadFile.kt b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/downloadFile.kt
deleted file mode 100644
index bbaeab5..0000000
--- a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/downloadFile.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-import java.net.http.HttpClient
-import kotlinx.coroutines.flow
-
-sealed class DownloadStatus {
-
- object Success : DownloadStatus()
-
- data class Error(val message: String) : DownloadStatus()
-
- data class Progress(val progress: Int): DownloadStatus()
-
-}
-
-// function from https://gist.githubusercontent.com/SG-K/63e379efcc3d1cd3ce4fb56ee0e29c42/raw/cd9a4a016401b7c54ec01303415b5871ffa26066/downloadFile.kt
-suspend fun HttpClient.downloadFile(file: File, url: String): Flow<DownloadStatus> {
- return flow {
- val response = call {
- url(url)
- method = HttpMethod.Get
- }.response
- val byteArray = ByteArray(response.contentLength()!!.toInt())
- var offset = 0
- do {
- val currentRead = response.content.readAvailable(byteArray, offset, byteArray.size)
- offset += currentRead
- val progress = (offset * 100f / byteArray.size).roundToInt()
- emit(DownloadStatus.Progress(progress))
- } while (currentRead > 0)
- response.close()
- if (response.status.isSuccess()) {
- file.writeBytes(byteArray)
- emit(DownloadStatus.Success)
- } else {
- emit(DownloadStatus.Error("File not downloaded"))
- }
- }
-} \ No newline at end of file
diff --git a/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/server/Server.kt b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/server/Server.kt
new file mode 100644
index 0000000..16f250e
--- /dev/null
+++ b/app/src/main/kotlin/xyz/brysonsteck/serverfordummies/server/Server.kt
@@ -0,0 +1,7 @@
+package xyz.brysonsteck.serverfordummies.server
+
+import java.io.File
+
+public class Server {
+
+} \ No newline at end of file
diff --git a/app/src/main/resources/xyz/brysonsteck/serverfordummies/dialog.fxml b/app/src/main/resources/xyz/brysonsteck/serverfordummies/dialog.fxml
new file mode 100644
index 0000000..6dab79a
--- /dev/null
+++ b/app/src/main/resources/xyz/brysonsteck/serverfordummies/dialog.fxml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.Pane?>
+
+
+<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1">
+ <children>
+ <Pane fx:id="iconPane" layoutX="14.0" layoutY="14.0" prefHeight="82.0" prefWidth="82.0" />
+ <Label fx:id="dialogLabel" layoutX="115.0" layoutY="40.0" text="Dialog&#10;Box" />
+ <ButtonBar layoutY="157.0" prefHeight="43.0" prefWidth="400.0">
+ <buttons>
+ <Button fx:id="otherButton" mnemonicParsing="false" text="Decline" />
+ <Button fx:id="defaultButton" defaultButton="true" mnemonicParsing="false" text="Accept" />
+ </buttons>
+ <padding>
+ <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
+ </padding>
+ </ButtonBar>
+ </children>
+</Pane>
diff --git a/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/info.png b/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/info.png
new file mode 100644
index 0000000..c1951a7
--- /dev/null
+++ b/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/info.png
Binary files differ
diff --git a/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/warning.png b/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/warning.png
new file mode 100644
index 0000000..4d66729
--- /dev/null
+++ b/app/src/main/resources/xyz/brysonsteck/serverfordummies/icons/warning.png
Binary files differ
diff --git a/app/src/main/resources/xyz/brysonsteck/serverfordummies/primary.fxml b/app/src/main/resources/xyz/brysonsteck/serverfordummies/primary.fxml
index 85b7370..c940d08 100644
--- a/app/src/main/resources/xyz/brysonsteck/serverfordummies/primary.fxml
+++ b/app/src/main/resources/xyz/brysonsteck/serverfordummies/primary.fxml
@@ -220,7 +220,7 @@
</Pane>
<ButtonBar fx:id="buttonBar" disable="true" layoutY="635.0" prefHeight="40.0" prefWidth="963.0">
<buttons>
- <Button mnemonicParsing="false" text="Build Server" />
+ <Button mnemonicParsing="false" onMouseClicked="#onBuild" text="Build Server" />
<Button defaultButton="true" mnemonicParsing="false" text="Start Server" />
</buttons>
<padding>
@@ -239,9 +239,9 @@
<Insets left="5.0" />
</HBox.margin>
</Label>
- <ProgressBar fx:id="progressBar" prefWidth="400.0" progress="0.0" visible="false">
+ <ProgressBar fx:id="progressBar" prefWidth="400.0" visible="false">
<HBox.margin>
- <Insets left="460.0" />
+ <Insets left="10.0" />
</HBox.margin>
</ProgressBar>
</children>