diff --git a/HMCL/build.gradle b/HMCL/build.gradle index ed9917765..23c91e8a4 100644 --- a/HMCL/build.gradle +++ b/HMCL/build.gradle @@ -2,6 +2,7 @@ import java.security.MessageDigest import java.util.jar.JarFile import java.util.jar.Pack200 import java.util.zip.GZIPOutputStream +import java.io.File if (!hasProperty('mainClass')) { ext.mainClass = 'org.jackhuang.hmcl.Main' @@ -48,7 +49,8 @@ jar { manifest { attributes 'Created-By' : 'Copyright(c) 2013-2017 huangyuhui.', - 'Main-Class' : mainClass + 'Main-Class' : mainClass, + 'Multi-Release': "true" } doLast { @@ -78,7 +80,7 @@ task makeExecutable(dependsOn: jar) doLast { ext { jar.classifier = '' makeExecutableinjar = jar.archivePath - jar.classifier = '' + jar.classifier = 'optimized' makeExecutableoutjar = jar.archivePath jar.classifier = '' } @@ -90,7 +92,7 @@ task makeExecutable(dependsOn: jar) doLast { while((read = is.read(bytes)) != -1) fos.write(bytes, 0, read); is.close() - is = new FileInputStream(makeExecutableinjar) + is = new FileInputStream(makeExecutableoutjar) while((read = is.read(bytes)) != -1) fos.write(bytes, 0, read); is.close() @@ -121,6 +123,42 @@ task makePackGZ(dependsOn: jar) doLast { fileEx.append sha1Hex } +task proguard(type: proguard.gradle.ProGuardTask, dependsOn: jar) { + ext { + def re = jar.classifier + injar = jar.archivePath + jar.classifier = 'optimized' + outjar = jar.archivePath + jar.classifier = re + } + + injars(/*["filter": "!META-INF/**,**"], */injar) + String javaHome = System.getProperty("java.home") + if (!javaHome.endsWith("jre")) + javaHome += File.separator + "jre" + libraryjars javaHome + "/lib/rt.jar" + libraryjars javaHome + "/lib/jce.jar" + libraryjars javaHome + "/lib/jsse.jar" + libraryjars javaHome + "/lib/ext/jfxrt.jar" + outjars outjar + + configuration 'proguard.pro' +} + +task combineMETA_INF(type: Jar) { + ext { + def re = jar.classifier + injar = jar.archivePath + jar.classifier = 'optimized' + outjar = jar.archivePath + jar.classifier = re + } + from zipTree(outjar) + from zipTree(injar).matching { + include "META-INF/" + } +} + build.dependsOn makeExecutable //build.dependsOn makePackGZ //Kotlin classes will be ignored by Pack200 due to class unrecognized attribute \ No newline at end of file diff --git a/HMCL/proguard.pro b/HMCL/proguard.pro new file mode 100644 index 000000000..b6615f366 --- /dev/null +++ b/HMCL/proguard.pro @@ -0,0 +1,15 @@ +-target 1.8 +-dontoptimize +-dontobfuscate + +# JFoenix +-keep class com.jfoenix.** { + ; + ; +} + +# HMCL +-keep class org.jackhuang.** { + ; + ; +} \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/Main.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/Main.kt index 1e740a984..c2abc6373 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/Main.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/Main.kt @@ -42,19 +42,15 @@ fun i18n(key: String): String { class Main : Application() { override fun start(stage: Stage) { - println(System.currentTimeMillis()) // When launcher visibility is set to "hide and reopen" without [Platform.implicitExit] = false, // Stage.show() cannot work again because JavaFX Toolkit have already shut down. Platform.setImplicitExit(false) Controllers.initialize(stage) - println("Showing stage: " + System.currentTimeMillis()) - stage.isResizable = false stage.scene = Controllers.scene stage.show() - println("Showed stage: " + System.currentTimeMillis()) } companion object { diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/LauncherHelper.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/LauncherHelper.kt index bcc51226c..9f85e5792 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/LauncherHelper.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/LauncherHelper.kt @@ -47,7 +47,6 @@ object LauncherHelper { val account = Settings.selectedAccount ?: throw IllegalStateException("No account here") val version = repository.getVersion(selectedVersion) val setting = profile.getVersionSetting(selectedVersion) - var finished = 0 Controllers.dialog(launchingStepsPane) task(Scheduler.JAVAFX) { emitStatus(LoadingState.DEPENDENCIES) } @@ -86,13 +85,15 @@ object LauncherHelper { .executor() .apply { taskListener = object : TaskListener { + var finished = 0 + override fun onFinished(task: Task) { ++finished runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / totTask.get() } } override fun onTerminate() { - runOnUiThread { Controllers.closeDialog() } + runOnUiThread(Controllers::closeDialog) } } }.start() diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/Profile.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/Profile.kt index 2d4fb2166..cd7115694 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/Profile.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/Profile.kt @@ -48,7 +48,7 @@ class Profile(name: String = "Default", initialGameDir: File = File(".minecraft" init { gameDirProperty.onChange { repository.changeDirectory(it!!) } - selectedVersionProperty.addListener { _ -> verifySelectedVersion() } + selectedVersionProperty.onInvalidated(this::verifySelectedVersion) EVENT_BUS.channel() += { event -> if (event.source == repository) verifySelectedVersion() } } diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/FXUtils.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/FXUtils.kt index 2052db664..f9730e748 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/FXUtils.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/FXUtils.kt @@ -55,7 +55,7 @@ fun Node.loadFXML(absolutePath: String) { } fun ListView<*>.smoothScrolling() { - skinProperty().addListener { _ -> + skinProperty().onInvalidated { val bar = lookup(".scroll-bar") as ScrollBar val virtualFlow = lookup(".virtual-flow") val frictions = doubleArrayOf(0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001) @@ -85,7 +85,7 @@ fun ListView<*>.smoothScrolling() { for (i in derivatives.indices) { derivatives[i] *= frictions[i] } - for (i in 1..derivatives.size - 1) { + for (i in 1 until derivatives.size) { derivatives[i] += derivatives[i - 1] } val dy = derivatives[derivatives.size - 1] @@ -243,12 +243,12 @@ fun Node.installTooltip(openDelay: Double = 1000.0, visibleDelay: Double = 5000. } fun JFXTextField.setValidateWhileTextChanged() { - textProperty().addListener { _ -> validate() } + textProperty().onInvalidated(this::validate) validate() } fun JFXPasswordField.setValidateWhileTextChanged() { - textProperty().addListener { _ -> validate() } + textProperty().onInvalidated(this::validate) validate() } diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt index 694fb38cd..3e383404c 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LeftPaneController.kt @@ -89,7 +89,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) { profilePane.children .filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> } - .forEach { (it as RipplerContainer).selected = (it.properties["profile"] as Pair).first == profile.name } + .forEach { (it as RipplerContainer).selected = (it.properties["profile"] as Pair<*, *>).first == profile.name } } fun onProfilesLoading() { diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LogWindow.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LogWindow.kt index fe4e6f787..1b7c745fd 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LogWindow.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/LogWindow.kt @@ -121,14 +121,14 @@ class LogWindow : Stage() { btnInfos.textProperty().bind(Bindings.createStringBinding(Callable { infoProperty.get().toString() + " infos" }, infoProperty)) btnDebugs.textProperty().bind(Bindings.createStringBinding(Callable { debugProperty.get().toString() + " debugs" }, debugProperty)) - btnFatals.selectedProperty().addListener(this::specificChanged) - btnErrors.selectedProperty().addListener(this::specificChanged) - btnWarns.selectedProperty().addListener(this::specificChanged) - btnInfos.selectedProperty().addListener(this::specificChanged) - btnDebugs.selectedProperty().addListener(this::specificChanged) + btnFatals.selectedProperty().onInvalidated(this::specificChanged) + btnErrors.selectedProperty().onInvalidated(this::specificChanged) + btnWarns.selectedProperty().onInvalidated(this::specificChanged) + btnInfos.selectedProperty().onInvalidated(this::specificChanged) + btnDebugs.selectedProperty().onInvalidated(this::specificChanged) } - private fun specificChanged(observable: Observable) { + private fun specificChanged() { var res = "" if (btnFatals.isSelected) res += "\"fatal\", " if (btnErrors.isSelected) res += "\"error\", " diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt index 38c0a6886..08e6215a0 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/MainPage.kt @@ -101,7 +101,7 @@ class MainPage : StackPane(), DecoratorPage { fun onProfileChanged(event: ProfileChangedEvent) = runOnUiThread { val profile = event.value - profile.selectedVersionProperty.setChangedListener { t -> + profile.selectedVersionProperty.setChangedListener { versionChanged(profile.selectedVersion) } loadVersions(profile) diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/RipplerContainer.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/RipplerContainer.kt index 88304cab2..5f16a69fd 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/RipplerContainer.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/RipplerContainer.kt @@ -35,6 +35,7 @@ import javafx.scene.paint.Paint import javafx.scene.shape.Rectangle import org.jackhuang.hmcl.util.getValue import org.jackhuang.hmcl.util.onChange +import org.jackhuang.hmcl.util.onInvalidated import org.jackhuang.hmcl.util.setValue import java.util.concurrent.Callable @@ -106,7 +107,7 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane() } } - pressedProperty().addListener { _ -> this.buttonRippler.hideOverlay() } + pressedProperty().onInvalidated(this.buttonRippler::hideOverlay) isPickOnBounds = false this.buttonContainer.isPickOnBounds = false this.buttonContainer.shapeProperty().bind(shapeProperty()) @@ -136,8 +137,8 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane() this.updateChildren() - containerProperty.addListener { _ -> updateChildren() } - selectedProperty.addListener { _ -> + containerProperty.onInvalidated(this::updateChildren) + selectedProperty.onInvalidated { if (selected) background = Background(BackgroundFill(ripplerFill, defaultRadii, null)) else background = Background(BackgroundFill(Color.TRANSPARENT, defaultRadii, null)) } diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/InstallersPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/InstallersPage.kt index 75f592833..6bc842e95 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/InstallersPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/InstallersPage.kt @@ -31,6 +31,7 @@ import org.jackhuang.hmcl.ui.construct.Validator import org.jackhuang.hmcl.ui.loadFXML import org.jackhuang.hmcl.ui.wizard.WizardController import org.jackhuang.hmcl.ui.wizard.WizardPage +import org.jackhuang.hmcl.util.onInvalidated class InstallersPage(private val controller: WizardController, private val repository: GameRepository, private val downloadProvider: DownloadProvider): StackPane(), WizardPage { @@ -50,9 +51,7 @@ class InstallersPage(private val controller: WizardController, private val repos val gameVersion = controller.settings["game"] as String txtName.validators += Validator { !repository.hasVersion(it) && it.isNotBlank() }.apply { message = i18n("version.already_exists") } - txtName.textProperty().addListener { _ -> - btnInstall.isDisable = !txtName.validate() - } + txtName.textProperty().onInvalidated { btnInstall.isDisable = !txtName.validate() } txtName.text = gameVersion btnForge.setOnMouseClicked { diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/ModpackPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/ModpackPage.kt index 5df12b668..f34eee7a0 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/ModpackPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/download/ModpackPage.kt @@ -34,6 +34,7 @@ import org.jackhuang.hmcl.ui.* import org.jackhuang.hmcl.ui.construct.Validator import org.jackhuang.hmcl.ui.wizard.WizardController import org.jackhuang.hmcl.ui.wizard.WizardPage +import org.jackhuang.hmcl.util.onInvalidated class ModpackPage(private val controller: WizardController): StackPane(), WizardPage { override val title: String = i18n("modpack.task.install") @@ -62,9 +63,7 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard controller.settings[MODPACK_FILE] = selectedFile lblModpackLocation.text = selectedFile.absolutePath txtModpackName.validators += Validator { !profile.repository.hasVersion(it) && it.isNotBlank() }.apply { message = i18n("version.already_exists") } - txtModpackName.textProperty().addListener { _ -> - btnInstall.isDisable = !txtModpackName.validate() - } + txtModpackName.textProperty().onInvalidated { btnInstall.isDisable = !txtModpackName.validate() } try { manifest = readModpackManifest(selectedFile) diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/export/ModpackInfoPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/export/ModpackInfoPage.kt index ac0ec6460..60029474f 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/export/ModpackInfoPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/export/ModpackInfoPage.kt @@ -34,6 +34,7 @@ import org.jackhuang.hmcl.ui.loadFXML import org.jackhuang.hmcl.ui.smoothScrolling import org.jackhuang.hmcl.ui.wizard.WizardController import org.jackhuang.hmcl.ui.wizard.WizardPage +import org.jackhuang.hmcl.util.onInvalidated class ModpackInfoPage(private val controller: WizardController, version: String): StackPane(), WizardPage { override val title: String = i18n("modpack.wizard.step.1.title") @@ -50,14 +51,14 @@ class ModpackInfoPage(private val controller: WizardController, version: String) loadFXML("/assets/fxml/modpack/info.fxml") scroll.smoothScrolling() txtModpackName.text = version - txtModpackName.textProperty().addListener { _ -> checkValidation() } - txtModpackAuthor.textProperty().addListener { _ -> checkValidation() } - txtModpackVersion.textProperty().addListener { _ -> checkValidation() } + txtModpackName.textProperty().onInvalidated(this::checkValidation) + txtModpackAuthor.textProperty().onInvalidated(this::checkValidation) + txtModpackVersion.textProperty().onInvalidated(this::checkValidation) txtModpackAuthor.text = Settings.selectedAccount?.username ?: "" lblVersionName.text = version } - fun checkValidation() { + private fun checkValidation() { btnNext.isDisable = !txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate() } diff --git a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/launch/Log4jHandler.kt b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/launch/Log4jHandler.kt index f353d9c22..854067f81 100644 --- a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/launch/Log4jHandler.kt +++ b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/launch/Log4jHandler.kt @@ -74,7 +74,14 @@ internal class Log4jHandler(private val callback: (String, Log4jLevel) -> Unit) */ fun newLine(content: String) = Scheduler.COMPUTATION.schedule { - outputStream.write((content + OS.LINE_SEPARATOR).replace("log4j:Event", "log4j_Event").replace("log4j:Message", "log4j_Message").replace("log4j:Throwable", "log4j_Throwable").toByteArray()) + var log = content + if (!log.trim().startsWith("<")) + log = "", "") + "]]>" + outputStream.write((log + OS.LINE_SEPARATOR) + .replace("log4j:Event", "log4j_Event") + .replace("log4j:Message", "log4j_Message") + .replace("log4j:Throwable", "log4j_Throwable") + .toByteArray()) outputStream.flush() } diff --git a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt index 36f9b6b27..9af969dab 100644 --- a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt +++ b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt @@ -32,3 +32,5 @@ fun ObservableDoubleValue.onChange(op: (Double) -> Unit) = apply { addListener { fun ObservableList.onChange(op: (ListChangeListener.Change) -> Unit) = apply { addListener(ListChangeListener { op(it) }) } + +fun ObservableValue<*>.onInvalidated(op: () -> T) = apply { addListener { _ -> op() } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index cdaea85d1..972878c78 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'net.sf.proguard:proguard-gradle:5.3.3' } } @@ -61,6 +62,7 @@ allprojects { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "com.google.code.gson:gson:2.8.1" compile "org.apache.commons:commons-compress:1.8.1" + compile "org.tukaani:xz:1.6" testCompile group: 'junit', name: 'junit', version: '4.12' }