diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.kt b/src/main/java/de/bixilon/minosoft/Minosoft.kt index 4d294ebb0..394913b72 100644 --- a/src/main/java/de/bixilon/minosoft/Minosoft.kt +++ b/src/main/java/de/bixilon/minosoft/Minosoft.kt @@ -24,6 +24,7 @@ import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.versions.Version import de.bixilon.minosoft.data.registries.versions.Versions import de.bixilon.minosoft.gui.eros.Eros +import de.bixilon.minosoft.gui.eros.XStartOnFirstThreadWarning import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash import de.bixilon.minosoft.gui.eros.util.JavaFXInitializer import de.bixilon.minosoft.gui.rendering.Rendering @@ -69,13 +70,15 @@ object Minosoft { private set - @JvmStatic fun main(args: Array) { CommandLineArguments.parse(args) Util.initUtilClasses() Log.log(LogMessageType.OTHER, LogLevels.INFO) { "Starting minosoft" } + if (OSUtil.OS == OSUtil.OSs.MAC && !RunConfiguration.X_START_ON_FIRST_THREAD_SET && !RunConfiguration.DISABLE_RENDERING) { + Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "You are using MacOS, but have not enabled -XstartOnFirstThread. Rendering will not work!" } + } GitInfo.load() val taskWorker = TaskWorker(criticalErrorHandler = { _, exception -> exception.crash() }) @@ -124,9 +127,9 @@ object Minosoft { taskWorker += Task(identifier = StartupTasks.INITIALIZE_CLI, executor = { CLI.initialize() }) - if (!RunConfiguration.DISABLE_EROS) { taskWorker += Task(identifier = StartupTasks.INITIALIZE_JAVAFX, executor = { JavaFXInitializer.start() }) + taskWorker += Task(identifier = StartupTasks.X_START_ON_FIRST_THREAD_WARNING, executor = { XStartOnFirstThreadWarning.show() }, dependencies = arrayOf(StartupTasks.LOAD_CONFIG, StartupTasks.LOAD_LANGUAGE_FILES, StartupTasks.INITIALIZE_JAVAFX)) // ToDo: Show start up progress window @@ -141,6 +144,7 @@ object Minosoft { initialized = true Log.log(LogMessageType.OTHER, LogLevels.INFO) { "All startup tasks executed!" } + GlobalEventMaster.fireEvent(FinishInitializingEvent()) RunConfiguration.AUTO_CONNECT_TO?.let { autoConnect(it) } diff --git a/src/main/java/de/bixilon/minosoft/config/config/general/GeneralConfig.kt b/src/main/java/de/bixilon/minosoft/config/config/general/GeneralConfig.kt index e2bc766a3..79a2f46d4 100644 --- a/src/main/java/de/bixilon/minosoft/config/config/general/GeneralConfig.kt +++ b/src/main/java/de/bixilon/minosoft/config/config/general/GeneralConfig.kt @@ -23,4 +23,5 @@ data class GeneralConfig( @Json(name = "log") var log: MutableMap = LogMessageType.DEFAULT_LOG_MAP.toMutableMap(), @Json(name = "reduce_protocol_log") var reduceProtocolLog: Boolean = true, var language: String = "en_US", + @Json(name = "ignore_x_start_on_first_thread_warning") var ignoreXStartOnFirstThreadWarning: Boolean = false, ) diff --git a/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponents.kt b/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponents.kt index 612ef4598..dbeb19821 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponents.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponents.kt @@ -23,6 +23,7 @@ object TranslatableComponents { val GENERAL_CANCEL = "minosoft:general.cancel".toResourceLocation() val GENERAL_CONFIRM = "minosoft:general.confirm".toResourceLocation() val GENERAL_DELETE = "minosoft:general.delete".toResourceLocation() + val GENERAL_IGNORE = "minosoft:general.ignore".toResourceLocation() val EROS_DELETE_SERVER_CONFIRM_DESCRIPTION = { name: ChatComponent, address: String -> Minosoft.LANGUAGE_MANAGER.translate("minosoft:server_info.delete.dialog.description".toResourceLocation(), null, name, address) } val ACCOUNT_CARD_CONNECTION_COUNT = { count: Int -> Minosoft.LANGUAGE_MANAGER.translate("minosoft:main.account.card.connection_count".toResourceLocation(), null, count) } diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/Eros.kt b/src/main/java/de/bixilon/minosoft/gui/eros/Eros.kt index f52edc6bd..d3f102623 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/Eros.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/Eros.kt @@ -26,13 +26,22 @@ object Eros { lateinit var mainErosController: MainErosController + var skipErosStartup = false + init { GlobalEventMaster.registerEvent(CallbackEventInvoker.of { - JavaFXUtil.runLater { - mainErosController = JavaFXUtil.openModal(TITLE, LAYOUT) - mainErosController.stage.show() + if (skipErosStartup) { + return@of } + start() }) } + + fun start() { + JavaFXUtil.runLater { + mainErosController = JavaFXUtil.openModal(TITLE, LAYOUT) + mainErosController.stage.show() + } + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/XStartOnFirstThreadWarning.kt b/src/main/java/de/bixilon/minosoft/gui/eros/XStartOnFirstThreadWarning.kt new file mode 100644 index 000000000..8e2216583 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/eros/XStartOnFirstThreadWarning.kt @@ -0,0 +1,46 @@ +package de.bixilon.minosoft.gui.eros + +import de.bixilon.minosoft.Minosoft +import de.bixilon.minosoft.gui.eros.dialog.SimpleErosWarningDialog +import de.bixilon.minosoft.terminal.RunConfiguration +import de.bixilon.minosoft.util.KUtil.toResourceLocation +import de.bixilon.minosoft.util.OSUtil +import javafx.stage.Modality + +object XStartOnFirstThreadWarning { + + private fun showJavaFXRunningWarning() { + val dialog = SimpleErosWarningDialog( + title = "minosoft:x_start_on_first_thread_warning.eros_running.title".toResourceLocation(), + header = "minosoft:x_start_on_first_thread_warning.eros_running.header".toResourceLocation(), + description = "minosoft:x_start_on_first_thread_warning.eros_running.description".toResourceLocation(), + onIgnore = { Eros.start() }, + modality = Modality.APPLICATION_MODAL, + ) + dialog.show() + Eros.skipErosStartup = true + } + + @Synchronized + fun show() { + if (OSUtil.OS != OSUtil.OSs.MAC || RunConfiguration.DISABLE_RENDERING) { // ToDo + return + } + if (Minosoft.config.config.general.ignoreXStartOnFirstThreadWarning) { + return + } + if (RunConfiguration.X_START_ON_FIRST_THREAD_SET) { + return showJavaFXRunningWarning() + } + + val dialog = SimpleErosWarningDialog( + title = "minosoft:x_start_on_first_thread_warning.title".toResourceLocation(), + header = "minosoft:x_start_on_first_thread_warning.header".toResourceLocation(), + description = "minosoft:x_start_on_first_thread_warning.description".toResourceLocation(), + onIgnore = { Eros.start() }, + modality = Modality.APPLICATION_MODAL, + ) + dialog.show() + Eros.skipErosStartup = true + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/dialog/ErosErrorReport.kt b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/ErosErrorReport.kt index da7b301b2..6567ebd8b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/dialog/ErosErrorReport.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/ErosErrorReport.kt @@ -14,6 +14,7 @@ package de.bixilon.minosoft.gui.eros.dialog import de.bixilon.minosoft.Minosoft +import de.bixilon.minosoft.data.text.TranslatableComponents.GENERAL_IGNORE import de.bixilon.minosoft.gui.eros.controller.DialogController import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash import de.bixilon.minosoft.gui.eros.util.JavaFXUtil @@ -58,7 +59,7 @@ class ErosErrorReport : DialogController() { headerFX.text = HEADER descriptionFX.text = DESCRIPTION - ignoreFX.ctext = IGNORE + ignoreFX.ctext = GENERAL_IGNORE fatalCrashFX.ctext = FATAL_CRASH } @@ -68,7 +69,6 @@ class ErosErrorReport : DialogController() { private val TITLE = { exception: Throwable? -> Minosoft.LANGUAGE_MANAGER.translate("minosoft:error.title".toResourceLocation(), null, exception?.let { it::class.java.realName }) } private val HEADER = "minosoft:error.header".toResourceLocation() private val DESCRIPTION = "minosoft:error.description".toResourceLocation() - private val IGNORE = "minosoft:error.ignore".toResourceLocation() private val FATAL_CRASH = "minosoft:error.fatal_crash".toResourceLocation() fun Throwable?.report() { diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosConfirmationDialog.kt b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosConfirmationDialog.kt index db612f9e0..63ae32dee 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosConfirmationDialog.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosConfirmationDialog.kt @@ -25,6 +25,7 @@ import javafx.scene.control.Button import javafx.scene.input.KeyCode import javafx.scene.input.KeyEvent import javafx.scene.text.TextFlow +import javafx.stage.Modality class SimpleErosConfirmationDialog( val title: Any = DEFAULT_TITLE_TEXT, @@ -34,6 +35,7 @@ class SimpleErosConfirmationDialog( val confirmButtonText: Any = DEFAULT_CONFIRM_TEXT, val onCancel: () -> Unit = {}, val onConfirm: () -> Unit, + val modality: Modality = Modality.WINDOW_MODAL, ) : DialogController() { @FXML private lateinit var headerFX: TextFlow @FXML private lateinit var descriptionFX: TextFlow @@ -42,7 +44,7 @@ class SimpleErosConfirmationDialog( fun show() { JavaFXUtil.runLater { - JavaFXUtil.openModal(title, LAYOUT, this) + JavaFXUtil.openModal(title, LAYOUT, this, modality) stage.show() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosWarningDialog.kt b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosWarningDialog.kt new file mode 100644 index 000000000..6407ac6ea --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/eros/dialog/SimpleErosWarningDialog.kt @@ -0,0 +1,79 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.eros.dialog + +import de.bixilon.minosoft.Minosoft +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.gui.eros.controller.DialogController +import de.bixilon.minosoft.gui.eros.util.JavaFXUtil +import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.text +import de.bixilon.minosoft.util.KUtil.toResourceLocation +import de.bixilon.minosoft.util.task.pool.DefaultThreadPool +import javafx.fxml.FXML +import javafx.scene.control.Button +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyEvent +import javafx.scene.text.TextFlow +import javafx.stage.Modality + +class SimpleErosWarningDialog( + val title: Any = DEFAULT_TITLE_TEXT, + val header: Any = DEFAULT_TITLE_TEXT, + val description: Any? = null, + val ignoreButtonText: Any = DEFAULT_IGNORE_TEXT, + val onIgnore: () -> Unit = {}, + val modality: Modality = Modality.WINDOW_MODAL, +) : DialogController() { + @FXML private lateinit var headerFX: TextFlow + @FXML private lateinit var descriptionFX: TextFlow + @FXML private lateinit var ignoreButtonFX: Button + + fun show() { + JavaFXUtil.runLater { + JavaFXUtil.openModal(title, LAYOUT, this, modality) + stage.show() + } + } + + override fun init() { + headerFX.text = Minosoft.LANGUAGE_MANAGER.translate(header) + descriptionFX.text = description?.let { Minosoft.LANGUAGE_MANAGER.translate(it) } ?: ChatComponent.EMPTY + ignoreButtonFX.text = Minosoft.LANGUAGE_MANAGER.translate(ignoreButtonText).message + } + + override fun postInit() { + stage.setOnCloseRequest { + DefaultThreadPool += onIgnore + } + + stage.scene.root.addEventFilter(KeyEvent.KEY_PRESSED) { + if (it.code == KeyCode.ESCAPE) { + ignore() + } + } + } + + @FXML + fun ignore() { + DefaultThreadPool += onIgnore + stage.close() + } + + + companion object { + private val LAYOUT = "minosoft:eros/dialog/simple_warning.fxml".toResourceLocation() + private val DEFAULT_TITLE_TEXT = "minosoft:general.dialog.warning".toResourceLocation() + private val DEFAULT_IGNORE_TEXT = "minosoft:general.ignore".toResourceLocation() + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt index 5cb1cd48b..3ae04ed6e 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt @@ -217,7 +217,6 @@ class ParticleRenderer( companion object : RendererBuilder { override val RESOURCE_LOCATION = ResourceLocation("minosoft:particle") - override fun build(connection: PlayConnection, renderWindow: RenderWindow): ParticleRenderer { return ParticleRenderer(connection, renderWindow) } diff --git a/src/main/java/de/bixilon/minosoft/terminal/RunConfiguration.kt b/src/main/java/de/bixilon/minosoft/terminal/RunConfiguration.kt index 0fc24f391..a9d149926 100644 --- a/src/main/java/de/bixilon/minosoft/terminal/RunConfiguration.kt +++ b/src/main/java/de/bixilon/minosoft/terminal/RunConfiguration.kt @@ -18,6 +18,7 @@ import de.bixilon.minosoft.config.StaticConfiguration import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.util.OSUtil import java.io.File +import java.lang.management.ManagementFactory object RunConfiguration { var CONFIG_FILENAME = "minosoft.json" // Filename of minosoft's base configuration (located in AppData/Minosoft/config) @@ -56,6 +57,8 @@ object RunConfiguration { val TEMPORARY_FOLDER = System.getProperty("java.io.tmpdir", "$HOME_DIRECTORY/tmp/") + "/" + val X_START_ON_FIRST_THREAD_SET = ManagementFactory.getRuntimeMXBean().inputArguments.contains("-XstartOnFirstThread") + var VERSION_STRING = "Minosoft ${StaticConfiguration.VERSION}" var SKIP_RENDERERS: List = listOf() diff --git a/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt b/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt index c4f56e7c3..5b5d01fd7 100644 --- a/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt +++ b/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt @@ -22,5 +22,6 @@ enum class StartupTasks { LOAD_MODS, INITIALIZE_CLI, INITIALIZE_JAVAFX, + X_START_ON_FIRST_THREAD_WARNING, ; } diff --git a/src/main/java/de/bixilon/minosoft/util/task/worker/TaskWorker.kt b/src/main/java/de/bixilon/minosoft/util/task/worker/TaskWorker.kt index d472ccb4f..54fe61bcc 100644 --- a/src/main/java/de/bixilon/minosoft/util/task/worker/TaskWorker.kt +++ b/src/main/java/de/bixilon/minosoft/util/task/worker/TaskWorker.kt @@ -18,6 +18,9 @@ import de.bixilon.minosoft.util.KUtil.synchronizedMapOf import de.bixilon.minosoft.util.KUtil.synchronizedSetOf import de.bixilon.minosoft.util.KUtil.toSynchronizedList import de.bixilon.minosoft.util.KUtil.toSynchronizedMap +import de.bixilon.minosoft.util.logging.Log +import de.bixilon.minosoft.util.logging.LogLevels +import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.task.pool.DefaultThreadPool import de.bixilon.minosoft.util.task.pool.ThreadPoolRunnable import de.bixilon.minosoft.util.task.worker.tasks.Task @@ -32,7 +35,13 @@ class TaskWorker( operator fun plusAssign(task: Task) { check(state == TaskWorkerStates.PREPARING) { "Task worker is already working!" } - todo[task.identifier] = task + if (task.dependencies.contains(task.identifier)) { + throw IllegalArgumentException("Task can not depend on itself!") + } + val previous = todo.put(task.identifier, task) + if (previous != null) { + Log.log(LogMessageType.OTHER, LogLevels.WARN) { "Task ${task.identifier} replaced existing task!" } + } } diff --git a/src/main/resources/assets/minosoft/eros/dialog/simple_warning.fxml b/src/main/resources/assets/minosoft/eros/dialog/simple_warning.fxml new file mode 100644 index 000000000..2b67ae73e --- /dev/null +++ b/src/main/resources/assets/minosoft/eros/dialog/simple_warning.fxml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/minosoft/language/en_us.lang b/src/main/resources/assets/minosoft/language/en_us.lang index 5b2a86f2e..ca5c06f62 100644 --- a/src/main/resources/assets/minosoft/language/en_us.lang +++ b/src/main/resources/assets/minosoft/language/en_us.lang @@ -2,6 +2,7 @@ minosoft:general.empty= minosoft:general.cancel=Cancel minosoft:general.confirm=Confirm minosoft:general.delete=Delete +minosoft:general.ignore=Ignore minosoft:eros_window_title=Minosoft @@ -32,6 +33,7 @@ minosoft:connection.status.state.error=Error in connection! minosoft:general.dialog.are_you_sure=Are you sure? +minosoft:general.dialog.warning=Warning! minosoft:update_server.name.label=Server name @@ -104,5 +106,13 @@ minosoft:connection.login_kick.description=You got kicked while logging in from minosoft:error.title=%1$s - Minosoft minosoft:error.header=An error occurred! minosoft:error.description=An error in minosoft occurred. You can continue like before, but the behavior might not be the expected one. If this error persists, feel free to open an issue here: https://gitlab.bixilon.de/bixilon/minosoft/-/issues/ -minosoft:error.ignore=Ignore minosoft:error.fatal_crash=Fatal crash + + +minosoft:x_start_on_first_thread_warning.title=-XstartOnFirstThread not set +minosoft:x_start_on_first_thread_warning.header=-XstartOnFirstThread is not set +minosoft:x_start_on_first_thread_warning.description=It looks like you are using MacOS. Due to some design decisions made by apple, you have to set §7-XstartOnFirstThread§r as JVM Argument if you plan to use rendering. If you don't know how or just think this is shit, take a look at https://gitlab.bixilon.de/bixilon/minosoft/-/issues/29 + +minosoft:x_start_on_first_thread_warning.eros_running.title=Eros is running +minosoft:x_start_on_first_thread_warning.eros_running.header=Eros is running +minosoft:x_start_on_first_thread_warning.eros_running.description=It looks like eros is running and you plan to use rendering. This is (on MacOS) currently not possible. You have to set §7-XstartOnFirstThread §ras jvm argument and use the §7auto-connect §rcommand line option to connect to a server. Sorry for this. If you don't know how or just think that this is shit. take a look at https://gitlab.bixilon.de/bixilon/minosoft/-/issues/29