MacOS: warn if eros enabled or -XstartOnFirstThread is not set, #29

This commit is contained in:
Bixilon 2021-11-29 19:53:10 +01:00
parent a1035542db
commit c91f18d73d
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
14 changed files with 252 additions and 11 deletions

View File

@ -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<String>) {
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) }

View File

@ -23,4 +23,5 @@ data class GeneralConfig(
@Json(name = "log") var log: MutableMap<LogMessageType, LogLevels> = 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,
)

View File

@ -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) }

View File

@ -26,13 +26,22 @@ object Eros {
lateinit var mainErosController: MainErosController
var skipErosStartup = false
init {
GlobalEventMaster.registerEvent(CallbackEventInvoker.of<FinishInitializingEvent> {
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()
}
}
}

View File

@ -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
}
}

View File

@ -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() {

View File

@ -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()
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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()
}
}

View File

@ -217,7 +217,6 @@ class ParticleRenderer(
companion object : RendererBuilder<ParticleRenderer> {
override val RESOURCE_LOCATION = ResourceLocation("minosoft:particle")
override fun build(connection: PlayConnection, renderWindow: RenderWindow): ParticleRenderer {
return ParticleRenderer(connection, renderWindow)
}

View File

@ -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<ResourceLocation> = listOf()

View File

@ -22,5 +22,6 @@ enum class StartupTasks {
LOAD_MODS,
INITIALIZE_CLI,
INITIALIZE_JAVAFX,
X_START_ON_FIRST_THREAD_WARNING,
;
}

View File

@ -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!" }
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.text.TextFlow?>
<?import org.kordamp.ikonli.javafx.FontIcon?>
<HBox xmlns:fx="http://javafx.com/fxml/1" maxHeight="-Infinity" prefHeight="230.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16"> <!--fx:controller="de.bixilon.minosoft.gui.eros.dialog.SimpleErosWarningDialog" -->
<GridPane HBox.hgrow="ALWAYS">
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="ALWAYS"/>
<RowConstraints vgrow="NEVER"/>
</rowConstraints>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="NEVER"/>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
<GridPane GridPane.columnIndex="1">
<rowConstraints>
<RowConstraints vgrow="NEVER"/>
<RowConstraints vgrow="ALWAYS"/>
</rowConstraints>
<GridPane.margin>
<Insets top="50.0"/>
</GridPane.margin>
<TextFlow fx:id="descriptionFX" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="5.0" right="5.0"/>
</GridPane.margin>
<Text text="This is a really important warning, please click on cancel!"/>
</TextFlow>
<TextFlow fx:id="headerFX" style="-fx-font-size: 30;">
<GridPane.margin>
<Insets bottom="20.0" left="5.0" right="5.0"/>
</GridPane.margin>
<opaqueInsets>
<Insets/>
</opaqueInsets>
<Text text="Are you sure?"/>
</TextFlow>
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
</GridPane>
<FontIcon iconColor="#e4e44b" iconLiteral="fas-exclamation-triangle" iconSize="150">
<GridPane.margin>
<Insets left="5.0" right="10.0"/>
</GridPane.margin>
</FontIcon>
<rowConstraints>
<RowConstraints vgrow="ALWAYS"/>
</rowConstraints>
</GridPane>
<GridPane GridPane.rowIndex="1">
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS"/>
<ColumnConstraints hgrow="NEVER"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="NEVER"/>
</rowConstraints>
<Button fx:id="ignoreButtonFX" mnemonicParsing="false" onAction="#ignore" text="Ignore" GridPane.columnIndex="1">
<GridPane.margin>
<Insets left="5.0" right="5.0"/>
</GridPane.margin>
</Button>
<GridPane.margin>
<Insets bottom="10.0" right="5.0"/>
</GridPane.margin>
</GridPane>
</GridPane>
</HBox>

View File

@ -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