Launching progress
This commit is contained in:
@@ -17,27 +17,86 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.AuthenticationException
|
||||||
|
import org.jackhuang.hmcl.launch.DefaultLauncher
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackCompletionTask
|
import org.jackhuang.hmcl.mod.CurseForgeModpackCompletionTask
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.*
|
||||||
|
import org.jackhuang.hmcl.ui.Controllers
|
||||||
|
import org.jackhuang.hmcl.ui.DialogController
|
||||||
|
import org.jackhuang.hmcl.ui.LaunchingStepsPane
|
||||||
|
import org.jackhuang.hmcl.ui.runOnUiThread
|
||||||
|
|
||||||
|
|
||||||
object LauncherHelper {
|
object LauncherHelper {
|
||||||
|
val launchingStepsPane = LaunchingStepsPane()
|
||||||
|
|
||||||
fun launch() {
|
fun launch() {
|
||||||
val profile = Settings.selectedProfile
|
val profile = Settings.selectedProfile
|
||||||
val repository = profile.repository
|
val repository = profile.repository
|
||||||
val dependency = profile.dependency
|
val dependency = profile.dependency
|
||||||
val account = Settings.selectedAccount ?: throw IllegalStateException("No account here")
|
val account = Settings.selectedAccount ?: throw IllegalStateException("No account here")
|
||||||
val version = repository.getVersion(profile.selectedVersion)
|
val version = repository.getVersion(profile.selectedVersion)
|
||||||
val launcher = HMCLGameLauncher(
|
var finished = 0
|
||||||
|
|
||||||
|
Controllers.dialog(launchingStepsPane)
|
||||||
|
task(Scheduler.JAVAFX) { emitStatus(LoadingState.DEPENDENCIES) }
|
||||||
|
.then(dependency.checkGameCompletionAsync(version))
|
||||||
|
|
||||||
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.MODS) })
|
||||||
|
.then(CurseForgeModpackCompletionTask(dependency, profile.selectedVersion))
|
||||||
|
|
||||||
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LOGIN) })
|
||||||
|
.then(task {
|
||||||
|
try {
|
||||||
|
it["account"] = account.logIn(Settings.proxy)
|
||||||
|
} catch (e: AuthenticationException) {
|
||||||
|
it["account"] = DialogController.logIn(account)
|
||||||
|
runOnUiThread { Controllers.dialog(launchingStepsPane) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LAUNCHING) })
|
||||||
|
.then(task {
|
||||||
|
it["launcher"] = HMCLGameLauncher(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
versionId = profile.selectedVersion,
|
versionId = profile.selectedVersion,
|
||||||
options = profile.getVersionSetting(profile.selectedVersion).toLaunchOptions(profile.gameDir),
|
options = profile.getVersionSetting(profile.selectedVersion).toLaunchOptions(profile.gameDir),
|
||||||
account = account.logIn(Settings.proxy)
|
account = it["account"]
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
.then { it.get<DefaultLauncher>("launcher").launchAsync() }
|
||||||
|
|
||||||
dependency.checkGameCompletionAsync(version)
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.DONE) })
|
||||||
.then(CurseForgeModpackCompletionTask(dependency, profile.selectedVersion))
|
.executor()
|
||||||
.then(launcher.launchAsync())
|
.apply {
|
||||||
.subscribe(Scheduler.JAVAFX) { println("lalala") }
|
taskListener = object : TaskListener {
|
||||||
|
override fun onFinished(task: Task) {
|
||||||
|
++finished
|
||||||
|
runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / totTask.get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTerminate() {
|
||||||
|
runOnUiThread { Controllers.closeDialog() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun end() {
|
||||||
|
runOnUiThread { Controllers.closeDialog() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emitStatus(state: LoadingState) {
|
||||||
|
launchingStepsPane.lblCurrentState.text = state.toString()
|
||||||
|
launchingStepsPane.lblSteps.text = "${state.ordinal + 1} / ${LoadingState.values().size}"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LoadingState {
|
||||||
|
DEPENDENCIES,
|
||||||
|
MODS,
|
||||||
|
LOGIN,
|
||||||
|
LAUNCHING,
|
||||||
|
DONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,10 +23,11 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
|||||||
import org.jackhuang.hmcl.task.FileDownloadTask
|
import org.jackhuang.hmcl.task.FileDownloadTask
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
|
import org.jackhuang.hmcl.ui.DialogController
|
||||||
import org.jackhuang.hmcl.util.toURL
|
import org.jackhuang.hmcl.util.toURL
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
|
||||||
object AccountSkin {
|
object AccountHelper {
|
||||||
val SKIN_DIR = Main.APPDATA.resolve("skins")
|
val SKIN_DIR = Main.APPDATA.resolve("skins")
|
||||||
|
|
||||||
fun loadSkins(proxy: Proxy = Settings.proxy) {
|
fun loadSkins(proxy: Proxy = Settings.proxy) {
|
||||||
@@ -49,7 +50,7 @@ object AccountSkin {
|
|||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
if (account.canLogIn && (account.selectedProfile == null || refresh))
|
if (account.canLogIn && (account.selectedProfile == null || refresh))
|
||||||
account.logIn(proxy)
|
DialogController.logIn(account)
|
||||||
val profile = account.selectedProfile ?: return
|
val profile = account.selectedProfile ?: return
|
||||||
val name = profile.name ?: return
|
val name = profile.name ?: return
|
||||||
val url = "http://skins.minecraft.net/MinecraftSkins/$name.png"
|
val url = "http://skins.minecraft.net/MinecraftSkins/$name.png"
|
||||||
@@ -18,10 +18,8 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import com.jfoenix.controls.JFXButton
|
||||||
import com.jfoenix.controls.JFXCheckBox
|
|
||||||
import com.jfoenix.controls.JFXProgressBar
|
import com.jfoenix.controls.JFXProgressBar
|
||||||
import com.jfoenix.controls.JFXRadioButton
|
import com.jfoenix.controls.JFXRadioButton
|
||||||
import com.jfoenix.effects.JFXDepthManager
|
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.geometry.Rectangle2D
|
import javafx.geometry.Rectangle2D
|
||||||
@@ -30,13 +28,15 @@ import javafx.scene.control.ToggleGroup
|
|||||||
import javafx.scene.effect.BlurType
|
import javafx.scene.effect.BlurType
|
||||||
import javafx.scene.effect.DropShadow
|
import javafx.scene.effect.DropShadow
|
||||||
import javafx.scene.image.ImageView
|
import javafx.scene.image.ImageView
|
||||||
|
import javafx.scene.layout.HBox
|
||||||
import javafx.scene.layout.Pane
|
import javafx.scene.layout.Pane
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.setting.AccountSkin
|
import org.jackhuang.hmcl.setting.AccountHelper
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
@@ -53,6 +53,7 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
|||||||
@FXML lateinit var lblType: Label
|
@FXML lateinit var lblType: Label
|
||||||
@FXML lateinit var pgsSkin: JFXProgressBar
|
@FXML lateinit var pgsSkin: JFXProgressBar
|
||||||
@FXML lateinit var portraitView: ImageView
|
@FXML lateinit var portraitView: ImageView
|
||||||
|
@FXML lateinit var buttonPane: HBox
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/account-item.fxml")
|
loadFXML("/assets/fxml/account-item.fxml")
|
||||||
@@ -78,10 +79,18 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
|||||||
lblUser.text = account.username
|
lblUser.text = account.username
|
||||||
lblType.text = accountType(account)
|
lblType.text = accountType(account)
|
||||||
|
|
||||||
if (account is YggdrasilAccount)
|
if (account is YggdrasilAccount) {
|
||||||
btnRefresh.setOnMouseClicked {
|
btnRefresh.setOnMouseClicked {
|
||||||
pgsSkin.isVisible = true
|
pgsSkin.isVisible = true
|
||||||
AccountSkin.refreshSkinAsync(account).subscribe(Scheduler.JAVAFX) { loadSkin() }
|
AccountHelper.refreshSkinAsync(account)
|
||||||
|
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
||||||
|
}
|
||||||
|
AccountHelper.loadSkinAsync(account)
|
||||||
|
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account is OfflineAccount) { // Offline Account cannot be refreshed,
|
||||||
|
buttonPane.children -= btnRefresh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +100,7 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
|||||||
pgsSkin.isVisible = false
|
pgsSkin.isVisible = false
|
||||||
val size = 8.0 * 4
|
val size = 8.0 * 4
|
||||||
portraitView.viewport = Rectangle2D(size, size, size, size)
|
portraitView.viewport = Rectangle2D(size, size, size, size)
|
||||||
portraitView.image = AccountSkin.getSkin(account, 4.0)
|
portraitView.image = AccountHelper.getSkin(account, 4.0)
|
||||||
portraitView.fitHeight = 32.0
|
portraitView.fitHeight = 32.0
|
||||||
portraitView.fitWidth = 32.0
|
portraitView.fitWidth = 32.0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import org.jackhuang.hmcl.auth.Account
|
|||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.AccountSkin
|
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.taskResult
|
import org.jackhuang.hmcl.task.taskResult
|
||||||
@@ -117,11 +116,6 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
Settings.deleteAccount(account.username)
|
Settings.deleteAccount(account.username)
|
||||||
Platform.runLater(this@AccountsPage::loadAccounts)
|
Platform.runLater(this@AccountsPage::loadAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account is YggdrasilAccount)
|
|
||||||
AccountSkin.loadSkinAsync(account).subscribe(Scheduler.JAVAFX) {
|
|
||||||
loadSkin()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +130,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
val username = txtUsername.text
|
val username = txtUsername.text
|
||||||
val password = txtPassword.text
|
val password = txtPassword.text
|
||||||
progressBar.isVisible = true
|
progressBar.isVisible = true
|
||||||
|
lblCreationWarning.text = ""
|
||||||
taskResult("create_account") {
|
taskResult("create_account") {
|
||||||
try {
|
try {
|
||||||
val account = when (type) {
|
val account = when (type) {
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXDialog
|
||||||
import javafx.fxml.FXMLLoader
|
import javafx.fxml.FXMLLoader
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.Scene
|
import javafx.scene.Scene
|
||||||
|
import javafx.scene.layout.Region
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
|
|
||||||
@@ -32,7 +34,6 @@ object Controllers {
|
|||||||
val versionPane = VersionPage()
|
val versionPane = VersionPage()
|
||||||
|
|
||||||
lateinit var leftPaneController: LeftPaneController
|
lateinit var leftPaneController: LeftPaneController
|
||||||
lateinit var sidePaneController: SidePaneController
|
|
||||||
|
|
||||||
lateinit var decorator: Decorator
|
lateinit var decorator: Decorator
|
||||||
|
|
||||||
@@ -42,7 +43,6 @@ object Controllers {
|
|||||||
decorator = Decorator(stage, mainPane, Main.TITLE, max = false)
|
decorator = Decorator(stage, mainPane, Main.TITLE, max = false)
|
||||||
decorator.showPage(null)
|
decorator.showPage(null)
|
||||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||||
sidePaneController = SidePaneController(decorator.sidePane, decorator.drawer)
|
|
||||||
|
|
||||||
decorator.isCustomMaximize = false
|
decorator.isCustomMaximize = false
|
||||||
|
|
||||||
@@ -54,6 +54,14 @@ object Controllers {
|
|||||||
stage.minHeight = 480.0
|
stage.minHeight = 480.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dialog(content: Region): JFXDialog {
|
||||||
|
return decorator.showDialog(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeDialog() {
|
||||||
|
decorator.dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
fun navigate(node: Node?) {
|
fun navigate(node: Node?) {
|
||||||
decorator.showPage(node)
|
decorator.showPage(node)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import com.jfoenix.controls.JFXButton
|
||||||
|
import com.jfoenix.controls.JFXDialog
|
||||||
import com.jfoenix.controls.JFXDrawer
|
import com.jfoenix.controls.JFXDrawer
|
||||||
import com.jfoenix.controls.JFXHamburger
|
import com.jfoenix.controls.JFXHamburger
|
||||||
import com.jfoenix.effects.JFXDepthManager
|
import com.jfoenix.effects.JFXDepthManager
|
||||||
@@ -64,6 +65,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
private var allowMove: Boolean = false
|
private var allowMove: Boolean = false
|
||||||
private var isDragging: Boolean = false
|
private var isDragging: Boolean = false
|
||||||
private var windowDecoratorAnimation: Timeline? = null
|
private var windowDecoratorAnimation: Timeline? = null
|
||||||
|
private var dialogShown = false
|
||||||
@FXML lateinit var contentPlaceHolder: StackPane
|
@FXML lateinit var contentPlaceHolder: StackPane
|
||||||
@FXML lateinit var drawerWrapper: StackPane
|
@FXML lateinit var drawerWrapper: StackPane
|
||||||
@FXML lateinit var titleContainer: BorderPane
|
@FXML lateinit var titleContainer: BorderPane
|
||||||
@@ -78,9 +80,9 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
@FXML lateinit var lblTitle: Label
|
@FXML lateinit var lblTitle: Label
|
||||||
@FXML lateinit var leftPane: AdvancedListBox
|
@FXML lateinit var leftPane: AdvancedListBox
|
||||||
@FXML lateinit var drawer: JFXDrawer
|
@FXML lateinit var drawer: JFXDrawer
|
||||||
@FXML lateinit var sidePane: AdvancedListBox
|
|
||||||
@FXML lateinit var titleBurgerContainer: StackPane
|
@FXML lateinit var titleBurgerContainer: StackPane
|
||||||
@FXML lateinit var titleBurger: JFXHamburger
|
@FXML lateinit var titleBurger: JFXHamburger
|
||||||
|
@FXML lateinit var dialog: JFXDialog
|
||||||
|
|
||||||
private val onCloseButtonActionProperty: ObjectProperty<Runnable> = SimpleObjectProperty(Runnable { Main.stop() })
|
private val onCloseButtonActionProperty: ObjectProperty<Runnable> = SimpleObjectProperty(Runnable { Main.stop() })
|
||||||
@JvmName("onCloseButtonActionProperty") get
|
@JvmName("onCloseButtonActionProperty") get
|
||||||
@@ -125,6 +127,11 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawerWrapper.children -= dialog
|
||||||
|
dialog.dialogContainer = drawerWrapper
|
||||||
|
dialog.setOnDialogClosed { dialogShown = false }
|
||||||
|
dialog.setOnDialogOpened { dialogShown = true }
|
||||||
|
|
||||||
if (!min) buttonsContainer.children.remove(btnMin)
|
if (!min) buttonsContainer.children.remove(btnMin)
|
||||||
if (!max) buttonsContainer.children.remove(btnMax)
|
if (!max) buttonsContainer.children.remove(btnMax)
|
||||||
|
|
||||||
@@ -136,25 +143,6 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
|
|
||||||
setOverflowHidden(lookup("#contentPlaceHolderRoot") as Pane)
|
setOverflowHidden(lookup("#contentPlaceHolderRoot") as Pane)
|
||||||
setOverflowHidden(drawerWrapper)
|
setOverflowHidden(drawerWrapper)
|
||||||
|
|
||||||
// init the title hamburger icon
|
|
||||||
drawer.setOnDrawerOpening {
|
|
||||||
val animation = titleBurger.getAnimation()
|
|
||||||
animation.setRate(1.0)
|
|
||||||
animation.play()
|
|
||||||
}
|
|
||||||
drawer.setOnDrawerClosing {
|
|
||||||
val animation = titleBurger.getAnimation()
|
|
||||||
animation.setRate(-1.0)
|
|
||||||
animation.play()
|
|
||||||
}
|
|
||||||
titleBurgerContainer.setOnMouseClicked({
|
|
||||||
if (drawer.isHidden || drawer.isHiding) {
|
|
||||||
drawer.open()
|
|
||||||
} else {
|
|
||||||
drawer.close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onMouseMoved(mouseEvent: MouseEvent) {
|
fun onMouseMoved(mouseEvent: MouseEvent) {
|
||||||
@@ -423,6 +411,13 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showDialog(content: Region): JFXDialog {
|
||||||
|
dialog.content = content
|
||||||
|
if (!dialogShown)
|
||||||
|
dialog.show()
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
||||||
this.category = category
|
this.category = category
|
||||||
wizardController.provider = wizardProvider
|
wizardController.provider = wizardProvider
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* 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 {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.Account
|
||||||
|
import org.jackhuang.hmcl.auth.AuthInfo
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.task.SilentException
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
object DialogController {
|
||||||
|
|
||||||
|
fun logIn(account: Account): AuthInfo? {
|
||||||
|
if (account is YggdrasilAccount) {
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
val res = AtomicReference<AuthInfo>(null)
|
||||||
|
runOnUiThread {
|
||||||
|
val pane = YggdrasilAccountLoginPane(account, success = {
|
||||||
|
res.set(it)
|
||||||
|
latch.countDown()
|
||||||
|
Controllers.closeDialog()
|
||||||
|
}, failed = {
|
||||||
|
latch.countDown()
|
||||||
|
Controllers.closeDialog()
|
||||||
|
})
|
||||||
|
pane.dialog = Controllers.dialog(pane)
|
||||||
|
}
|
||||||
|
latch.await()
|
||||||
|
if (res.get() == null)
|
||||||
|
throw SilentException()
|
||||||
|
else
|
||||||
|
return res.get()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,9 +98,7 @@ fun ListView<*>.smoothScrolling() {
|
|||||||
|
|
||||||
fun ScrollPane.smoothScrolling() = JFXScrollPane.smoothScrolling(this)
|
fun ScrollPane.smoothScrolling() = JFXScrollPane.smoothScrolling(this)
|
||||||
|
|
||||||
fun runOnUiThread(runnable: () -> Unit) = {
|
fun runOnUiThread(runnable: () -> Unit) = JFXUtilities.runInFX(runnable)
|
||||||
JFXUtilities.runInFX(runnable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun takeSnapshot(node: Parent, width: Double, height: Double): WritableImage {
|
fun takeSnapshot(node: Parent, width: Double, height: Double): WritableImage {
|
||||||
val scene = Scene(node, width, height)
|
val scene = Scene(node, width, height)
|
||||||
|
|||||||
@@ -17,9 +17,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXDrawer
|
import com.jfoenix.controls.JFXProgressBar
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.layout.StackPane
|
||||||
|
|
||||||
class SidePaneController(sidePane: AdvancedListBox, drawer: JFXDrawer) {
|
class LaunchingStepsPane(): StackPane() {
|
||||||
|
@FXML lateinit var pgsTasks: JFXProgressBar
|
||||||
|
@FXML lateinit var lblCurrentState: Label
|
||||||
|
@FXML lateinit var lblSteps: Label
|
||||||
init {
|
init {
|
||||||
|
loadFXML("/assets/fxml/launching-steps.fxml")
|
||||||
|
|
||||||
|
limitHeight(200.0)
|
||||||
|
limitWidth(400.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* 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 {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXDialog
|
||||||
|
import com.jfoenix.controls.JFXPasswordField
|
||||||
|
import com.jfoenix.controls.JFXProgressBar
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.layout.StackPane
|
||||||
|
import org.jackhuang.hmcl.auth.AuthInfo
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.i18n
|
||||||
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.taskResult
|
||||||
|
|
||||||
|
class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, private val success: (AuthInfo) -> Unit, private val failed: () -> Unit) : StackPane() {
|
||||||
|
@FXML lateinit var lblUsername: Label
|
||||||
|
@FXML lateinit var txtPassword: JFXPasswordField
|
||||||
|
@FXML lateinit var lblCreationWarning: Label
|
||||||
|
@FXML lateinit var progressBar: JFXProgressBar
|
||||||
|
lateinit var dialog: JFXDialog
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadFXML("/assets/fxml/yggdrasil-account-login.fxml")
|
||||||
|
|
||||||
|
lblUsername.text = oldAccount.username
|
||||||
|
txtPassword.setOnAction {
|
||||||
|
onAccept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAccept() {
|
||||||
|
val username = oldAccount.username
|
||||||
|
val password = txtPassword.text
|
||||||
|
progressBar.isVisible = true
|
||||||
|
lblCreationWarning.text = ""
|
||||||
|
taskResult("login") {
|
||||||
|
try {
|
||||||
|
val account = YggdrasilAccount.fromUsername(username, password)
|
||||||
|
account.logIn(Settings.proxy)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}.subscribe(Scheduler.JAVAFX) {
|
||||||
|
val account: Any = it["login"]
|
||||||
|
if (account is AuthInfo) {
|
||||||
|
success(account)
|
||||||
|
dialog.close()
|
||||||
|
} else if (account is InvalidCredentialsException) {
|
||||||
|
lblCreationWarning.text = i18n("login.wrong_password")
|
||||||
|
} else if (account is Exception) {
|
||||||
|
lblCreationWarning.text = account.javaClass.toString() + ": " + account.localizedMessage
|
||||||
|
}
|
||||||
|
progressBar.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCancel() {
|
||||||
|
failed()
|
||||||
|
dialog.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
|
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<left>
|
<left>
|
||||||
<HBox spacing="8">
|
<HBox fx:id="buttonPane" spacing="8">
|
||||||
<JFXButton fx:id="btnRefresh" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
<JFXButton fx:id="btnRefresh" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
||||||
<JFXButton fx:id="btnDelete" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
<JFXButton fx:id="btnDelete" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</JFXButton>
|
</JFXButton>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|
||||||
<JFXDialog fx:id="dialog" transitionType="CENTER">
|
<JFXDialog fx:id="dialog">
|
||||||
<StackPane>
|
<StackPane>
|
||||||
<JFXDialogLayout>
|
<JFXDialogLayout>
|
||||||
<heading>
|
<heading>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.shape.Rectangle?>
|
<?import javafx.scene.shape.Rectangle?>
|
||||||
<?import java.lang.String?>
|
|
||||||
<?import org.jackhuang.hmcl.ui.AdvancedListBox?>
|
<?import org.jackhuang.hmcl.ui.AdvancedListBox?>
|
||||||
|
<?import java.lang.String?>
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
type="StackPane"
|
type="StackPane"
|
||||||
xmlns:fx="http://javafx.com/fxml">
|
xmlns:fx="http://javafx.com/fxml">
|
||||||
@@ -18,13 +18,7 @@
|
|||||||
<BorderPane>
|
<BorderPane>
|
||||||
<center>
|
<center>
|
||||||
<StackPane fx:id="drawerWrapper">
|
<StackPane fx:id="drawerWrapper">
|
||||||
<JFXDrawer fx:id="drawer" defaultDrawerSize="200" direction="LEFT">
|
<JFXDialog fx:id="dialog" overlayClose="false" />
|
||||||
<styleClass>
|
|
||||||
<String fx:value="body"/>
|
|
||||||
</styleClass>
|
|
||||||
<sidePane>
|
|
||||||
<AdvancedListBox fx:id="sidePane" />
|
|
||||||
</sidePane>
|
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<left>
|
<left>
|
||||||
<StackPane minWidth="200" maxWidth="200" styleClass="jfx-decorator-content-container">
|
<StackPane minWidth="200" maxWidth="200" styleClass="jfx-decorator-content-container">
|
||||||
@@ -43,7 +37,8 @@
|
|||||||
</StackPane>
|
</StackPane>
|
||||||
</left>
|
</left>
|
||||||
<center>
|
<center>
|
||||||
<StackPane fx:id="contentPlaceHolderRoot" styleClass="jfx-decorator-content-container" VBox.vgrow="ALWAYS">
|
<StackPane fx:id="contentPlaceHolderRoot" styleClass="jfx-decorator-content-container"
|
||||||
|
VBox.vgrow="ALWAYS">
|
||||||
<StackPane fx:id="contentPlaceHolder" styleClass="jfx-decorator-content-container">
|
<StackPane fx:id="contentPlaceHolder" styleClass="jfx-decorator-content-container">
|
||||||
<styleClass>
|
<styleClass>
|
||||||
<String fx:value="jfx-decorator-content-container"/>
|
<String fx:value="jfx-decorator-content-container"/>
|
||||||
@@ -53,7 +48,6 @@
|
|||||||
</StackPane>
|
</StackPane>
|
||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</JFXDrawer>
|
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</center>
|
</center>
|
||||||
<top>
|
<top>
|
||||||
@@ -65,18 +59,8 @@
|
|||||||
<left>
|
<left>
|
||||||
<BorderPane minWidth="200" maxWidth="200" fx:id="titleWrapper">
|
<BorderPane minWidth="200" maxWidth="200" fx:id="titleWrapper">
|
||||||
<center>
|
<center>
|
||||||
<HBox>
|
<Label fx:id="lblTitle" BorderPane.alignment="CENTER" mouseTransparent="true"
|
||||||
<JFXRippler maskType="CIRCLE" style="-fx-ripple-color:WHITE;">
|
|
||||||
<StackPane fx:id="titleBurgerContainer" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30">
|
|
||||||
<JFXHamburger fx:id="titleBurger">
|
|
||||||
<HamburgerBackArrowBasicTransition/>
|
|
||||||
</JFXHamburger>
|
|
||||||
</StackPane>
|
|
||||||
</JFXRippler>
|
|
||||||
<Label fx:id="lblTitle" BorderPane.alignment="CENTER"
|
|
||||||
mouseTransparent="true"
|
|
||||||
style="-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 15px;"/>
|
style="-fx-background-color: transparent; -fx-text-fill: white; -fx-font-size: 15px;"/>
|
||||||
</HBox>
|
|
||||||
</center>
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<Rectangle height="${navBar.height}" width="1" fill="gray"/>
|
<Rectangle height="${navBar.height}" width="1" fill="gray"/>
|
||||||
@@ -87,7 +71,8 @@
|
|||||||
<BorderPane fx:id="navBar">
|
<BorderPane fx:id="navBar">
|
||||||
<left>
|
<left>
|
||||||
<HBox fx:id="navLeft" alignment="CENTER_LEFT" style="-fx-padding: 0 5 0 5;">
|
<HBox fx:id="navLeft" alignment="CENTER_LEFT" style="-fx-padding: 0 5 0 5;">
|
||||||
<JFXButton fx:id="backNavButton" onMouseClicked="#onBack" styleClass="jfx-decorator-button" ripplerFill="white">
|
<JFXButton fx:id="backNavButton" onMouseClicked="#onBack"
|
||||||
|
styleClass="jfx-decorator-button" ripplerFill="white">
|
||||||
<graphic>
|
<graphic>
|
||||||
<fx:include source="/assets/svg/arrow-left.fxml"/>
|
<fx:include source="/assets/svg/arrow-left.fxml"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
@@ -97,12 +82,14 @@
|
|||||||
</left>
|
</left>
|
||||||
<right>
|
<right>
|
||||||
<HBox fx:id="navRight" alignment="CENTER_LEFT">
|
<HBox fx:id="navRight" alignment="CENTER_LEFT">
|
||||||
<JFXButton fx:id="refreshNavButton" onMouseClicked="#onRefresh" styleClass="jfx-decorator-button" ripplerFill="white">
|
<JFXButton fx:id="refreshNavButton" onMouseClicked="#onRefresh"
|
||||||
|
styleClass="jfx-decorator-button" ripplerFill="white">
|
||||||
<graphic>
|
<graphic>
|
||||||
<fx:include source="/assets/svg/refresh.fxml"/>
|
<fx:include source="/assets/svg/refresh.fxml"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</JFXButton>
|
</JFXButton>
|
||||||
<JFXButton fx:id="closeNavButton" onMouseClicked="#onCloseNav" styleClass="jfx-decorator-button" ripplerFill="white">
|
<JFXButton fx:id="closeNavButton" onMouseClicked="#onCloseNav"
|
||||||
|
styleClass="jfx-decorator-button" ripplerFill="white">
|
||||||
<graphic>
|
<graphic>
|
||||||
<fx:include source="/assets/svg/close.fxml"/>
|
<fx:include source="/assets/svg/close.fxml"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|||||||
16
HMCL/src/main/resources/assets/fxml/launching-steps.fxml
Normal file
16
HMCL/src/main/resources/assets/fxml/launching-steps.fxml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXProgressBar?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
type="StackPane">
|
||||||
|
<JFXProgressBar fx:id="pgsTasks" StackPane.alignment="TOP_CENTER" />
|
||||||
|
<VBox alignment="CENTER">
|
||||||
|
<Label fx:id="lblCurrentState" style="-fx-font-size: 20px;" />
|
||||||
|
<Label fx:id="lblSteps" style="-fx-font-size: 14px;" />
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
</fx:root>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.*?>
|
||||||
|
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
type="StackPane">
|
||||||
|
<JFXDialogLayout>
|
||||||
|
<heading>
|
||||||
|
<Label text="%ui.message.enter_password" />
|
||||||
|
</heading>
|
||||||
|
<body>
|
||||||
|
<VBox spacing="15" style="-fx-padding: 15 0 0 0;">
|
||||||
|
<Label fx:id="lblUsername" />
|
||||||
|
<JFXPasswordField fx:id="txtPassword" promptText="%ui.label.password" labelFloat="true">
|
||||||
|
<validators>
|
||||||
|
<RequiredFieldValidator message="Input Required!">
|
||||||
|
</RequiredFieldValidator>
|
||||||
|
</validators>
|
||||||
|
</JFXPasswordField>
|
||||||
|
</VBox>
|
||||||
|
</body>
|
||||||
|
<actions>
|
||||||
|
<Label fx:id="lblCreationWarning" />
|
||||||
|
<JFXButton onMouseClicked="#onAccept" text="%button.ok" styleClass="dialog-accept"/>
|
||||||
|
<JFXButton onMouseClicked="#onCancel" text="%button.cancel" styleClass="dialog-cancel"/>
|
||||||
|
</actions>
|
||||||
|
</JFXDialogLayout>
|
||||||
|
<JFXProgressBar fx:id="progressBar" visible="false" StackPane.alignment="TOP_CENTER"/>
|
||||||
|
</fx:root>
|
||||||
@@ -167,8 +167,8 @@ ui.label.version=版本
|
|||||||
ui.label.password=密碼
|
ui.label.password=密碼
|
||||||
ui.label.profile=配置
|
ui.label.profile=配置
|
||||||
|
|
||||||
ui.message.first_load=請在左邊輸入您的帳號
|
ui.message.first_load=請輸入您的帳號
|
||||||
ui.message.enter_password=請在左邊輸入您的密碼
|
ui.message.enter_password=請輸入您的密碼
|
||||||
ui.message.launching=啟動中
|
ui.message.launching=啟動中
|
||||||
ui.message.making=生成中
|
ui.message.making=生成中
|
||||||
ui.message.sure_remove=真的要刪除配置%s嗎?
|
ui.message.sure_remove=真的要刪除配置%s嗎?
|
||||||
|
|||||||
@@ -167,8 +167,8 @@ ui.label.version=版本
|
|||||||
ui.label.password=密码
|
ui.label.password=密码
|
||||||
ui.label.profile=配置
|
ui.label.profile=配置
|
||||||
|
|
||||||
ui.message.first_load=请在左边输入您的账号
|
ui.message.first_load=请输入您的账号
|
||||||
ui.message.enter_password=请在左边输入您的密码
|
ui.message.enter_password=请输入您的密码
|
||||||
ui.message.launching=启动中
|
ui.message.launching=启动中
|
||||||
ui.message.making=生成中
|
ui.message.making=生成中
|
||||||
ui.message.sure_remove=真的要删除配置%s吗?
|
ui.message.sure_remove=真的要删除配置%s吗?
|
||||||
|
|||||||
@@ -43,20 +43,14 @@ class DefaultDependencyManager(override val repository: DefaultGameRepository, o
|
|||||||
|
|
||||||
override fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task {
|
override fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task {
|
||||||
if (libraryId == "forge")
|
if (libraryId == "forge")
|
||||||
return ForgeInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
return ForgeInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
val newVersion = task.result!!
|
.then { VersionJSONSaveTask(this, it["version"]) }
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
|
||||||
}
|
|
||||||
else if (libraryId == "liteloader")
|
else if (libraryId == "liteloader")
|
||||||
return LiteLoaderInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
return LiteLoaderInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
val newVersion = task.result!!
|
.then { VersionJSONSaveTask(this, it["version"]) }
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
|
||||||
}
|
|
||||||
else if (libraryId == "optifine")
|
else if (libraryId == "optifine")
|
||||||
return OptiFineInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
return OptiFineInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
val newVersion = task.result!!
|
.then { VersionJSONSaveTask(this, it["version"]) }
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
throw IllegalArgumentException("Library id $libraryId is unrecognized.")
|
throw IllegalArgumentException("Library id $libraryId is unrecognized.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
|||||||
|
|
||||||
override fun buildAsync(): Task {
|
override fun buildAsync(): Task {
|
||||||
val gameVersion = gameVersion
|
val gameVersion = gameVersion
|
||||||
return VersionJSONDownloadTask(gameVersion = gameVersion) then a@{ task ->
|
return VersionJSONDownloadTask(gameVersion, dependencyManager, "raw_version_json")
|
||||||
var version = GSON.fromJson<Version>(task.result!!) ?: return@a null
|
.then {
|
||||||
|
var version = GSON.fromJson<Version>(it["raw_version_json"])!!
|
||||||
|
it["version"] = version
|
||||||
version = version.copy(id = name, jar = null)
|
version = version.copy(id = name, jar = null)
|
||||||
var result = ParallelTask(
|
var result = ParallelTask(
|
||||||
GameAssetDownloadTask(dependencyManager, version),
|
GameAssetDownloadTask(dependencyManager, version),
|
||||||
@@ -39,42 +41,35 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
|||||||
) then VersionJSONSaveTask(dependencyManager, version)
|
) then VersionJSONSaveTask(dependencyManager, version)
|
||||||
|
|
||||||
if (toolVersions.containsKey("forge"))
|
if (toolVersions.containsKey("forge"))
|
||||||
result = result then libraryTaskHelper(gameVersion, version, "forge")
|
result = result then libraryTaskHelper(gameVersion, "forge")
|
||||||
if (toolVersions.containsKey("liteloader"))
|
if (toolVersions.containsKey("liteloader"))
|
||||||
result = result then libraryTaskHelper(gameVersion, version, "liteloader")
|
result = result then libraryTaskHelper(gameVersion, "liteloader")
|
||||||
if (toolVersions.containsKey("optifine"))
|
if (toolVersions.containsKey("optifine"))
|
||||||
result = result then libraryTaskHelper(gameVersion, version, "optifine")
|
result = result then libraryTaskHelper(gameVersion, "optifine")
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun libraryTaskHelper(gameVersion: String, version: Version, libraryId: String): Task.(Task) -> Task = { prev ->
|
private fun libraryTaskHelper(gameVersion: String, libraryId: String): (AutoTypingMap<String>) -> Task = {
|
||||||
var thisVersion = version
|
dependencyManager.installLibraryAsync(gameVersion, it["version"], libraryId, toolVersions[libraryId]!!)
|
||||||
if (prev is TaskResult<*> && prev.result is Version) {
|
|
||||||
thisVersion = prev.result as Version
|
|
||||||
}
|
|
||||||
dependencyManager.installLibraryAsync(gameVersion, thisVersion, libraryId, toolVersions[libraryId]!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class VersionJSONDownloadTask(val gameVersion: String): Task() {
|
private class VersionJSONDownloadTask(val gameVersion: String, val dependencyManager: DefaultDependencyManager, val id: String): Task() {
|
||||||
override val dependents: MutableCollection<Task> = LinkedList()
|
override val dependents: MutableCollection<Task> = LinkedList()
|
||||||
override val dependencies: MutableCollection<Task> = LinkedList()
|
override val dependencies: MutableCollection<Task> = LinkedList()
|
||||||
var httpTask: GetTask? = null
|
|
||||||
val result: String? get() = httpTask?.result
|
|
||||||
|
|
||||||
val gameVersionList: VersionList<*> = dependencyManager.getVersionList("game")
|
private val gameVersionList: VersionList<*> = dependencyManager.getVersionList("game")
|
||||||
init {
|
init {
|
||||||
if (!gameVersionList.loaded)
|
if (!gameVersionList.loaded)
|
||||||
dependents += gameVersionList.refreshAsync(downloadProvider)
|
dependents += gameVersionList.refreshAsync(dependencyManager.downloadProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val remoteVersion = gameVersionList.getVersions(gameVersion).firstOrNull()
|
val remoteVersion = gameVersionList.getVersions(gameVersion).firstOrNull()
|
||||||
?: throw Error("Cannot find specific version $gameVersion in remote repository")
|
?: throw Error("Cannot find specific version $gameVersion in remote repository")
|
||||||
|
|
||||||
val jsonURL = downloadProvider.injectURL(remoteVersion.url)
|
val jsonURL = dependencyManager.downloadProvider.injectURL(remoteVersion.url)
|
||||||
httpTask = GetTask(jsonURL.toURL(), proxy = dependencyManager.proxy)
|
dependencies += GetTask(jsonURL.toURL(), proxy = dependencyManager.proxy, id = id)
|
||||||
dependencies += httpTask!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
lateinit var remote: RemoteVersion<*>
|
lateinit var remote: RemoteVersion<*>
|
||||||
override val dependents = mutableListOf<Task>()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies = mutableListOf<Task>()
|
override val dependencies = mutableListOf<Task>()
|
||||||
override val id = ID
|
override val id = "version"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!forgeVersionList.loaded)
|
if (!forgeVersionList.loaded)
|
||||||
dependents += forgeVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
dependents += forgeVersionList.refreshAsync(dependencyManager.downloadProvider).then {
|
||||||
remote = forgeVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote forge version $gameVersion, $remoteVersion not found")
|
remote = forgeVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote forge version $gameVersion, $remoteVersion not found")
|
||||||
FileDownloadTask(remote.url.toURL(), installer)
|
FileDownloadTask(remote.url.toURL(), installer)
|
||||||
}
|
}
|
||||||
@@ -76,8 +76,4 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
|
|
||||||
check(installer.delete(), { "Unable to delete installer file $installer" })
|
check(installer.delete(), { "Unable to delete installer file $installer" })
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ID = "forge_install_task"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -37,11 +37,11 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
||||||
override val dependents = mutableListOf<Task>()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies = mutableListOf<Task>()
|
override val dependencies = mutableListOf<Task>()
|
||||||
override val id = ID
|
override val id = "version"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!liteLoaderVersionList.loaded)
|
if (!liteLoaderVersionList.loaded)
|
||||||
dependents += LiteLoaderVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
dependents += LiteLoaderVersionList.refreshAsync(dependencyManager.downloadProvider).then {
|
||||||
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $gameVersion, $remoteVersion not found")
|
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $gameVersion, $remoteVersion not found")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -70,8 +70,4 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
)
|
)
|
||||||
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ID = "lite_loader_install_task"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
|
|||||||
lateinit var remote: RemoteVersion<*>
|
lateinit var remote: RemoteVersion<*>
|
||||||
override val dependents = mutableListOf<Task>()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies = mutableListOf<Task>()
|
override val dependencies = mutableListOf<Task>()
|
||||||
override val id = ID
|
override val id = "version"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!optiFineVersionList.loaded)
|
if (!optiFineVersionList.loaded)
|
||||||
@@ -75,8 +75,4 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
|
|||||||
result = version.copy(libraries = merge(version.libraries, libraries), mainClass = mainClass, minecraftArguments = arg)
|
result = version.copy(libraries = merge(version.libraries, libraries), mainClass = mainClass, minecraftArguments = arg)
|
||||||
dependencies += GameLibrariesTask(dependencyManager, version.copy(libraries = libraries))
|
dependencies += GameLibrariesTask(dependencyManager, version.copy(libraries = libraries))
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ID = "optifine_install_task"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -17,14 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
internal class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(P) -> Task?, override val reliant: Boolean) : Task() {
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
|
|
||||||
|
internal class CoupleTask<P: Task>(pred: P, private val succ: (AutoTypingMap<String>) -> Task?, override val reliant: Boolean) : Task() {
|
||||||
override val hidden: Boolean = true
|
override val hidden: Boolean = true
|
||||||
|
|
||||||
override val dependents: Collection<Task> = listOf(pred)
|
override val dependents: Collection<Task> = listOf(pred)
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val task = this.succ(pred)
|
val task = this.succ(variables!!)
|
||||||
if (task != null)
|
if (task != null)
|
||||||
dependencies += task
|
dependencies += task
|
||||||
}
|
}
|
||||||
@@ -33,9 +35,9 @@ internal class CoupleTask<P: Task>(private val pred: P, private val succ: Task.(
|
|||||||
/**
|
/**
|
||||||
* @param b A runnable that decides what to do next, You can also do something here.
|
* @param b A runnable that decides what to do next, You can also do something here.
|
||||||
*/
|
*/
|
||||||
infix fun <T: Task> T.then(b: Task.(T) -> Task?): Task = CoupleTask(this, b, true)
|
infix fun <T: Task> T.then(b: (AutoTypingMap<String>) -> Task?): Task = CoupleTask(this, b, true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param b A runnable that decides what to do next, You can also do something here.
|
* @param b A runnable that decides what to do next, You can also do something here.
|
||||||
*/
|
*/
|
||||||
infix fun <T: Task> T.with(b: Task.(T) -> Task?): Task = CoupleTask(this, b, false)
|
infix fun <T: Task> T.with(b: (AutoTypingMap<String>) -> Task?): Task = CoupleTask(this, b, false)
|
||||||
@@ -25,9 +25,8 @@ import java.net.Proxy
|
|||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY): TaskResult<String>() {
|
class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY, override val id: String = ID): TaskResult<String>() {
|
||||||
override val scheduler: Scheduler = Scheduler.IO
|
override val scheduler: Scheduler = Scheduler.IO
|
||||||
override val id = ID
|
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
var exception: IOException? = null
|
var exception: IOException? = null
|
||||||
|
|||||||
@@ -81,11 +81,15 @@ abstract class Task {
|
|||||||
protected fun updateProgress(progress: Double) {
|
protected fun updateProgress(progress: Double) {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - lastTime >= progressInterval) {
|
if (now - lastTime >= progressInterval) {
|
||||||
progressPropertyImpl.updateAsync(progress, progressUpdate)
|
updateProgressImmediately(progress)
|
||||||
lastTime = now
|
lastTime = now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun updateProgressImmediately(progress: Double) {
|
||||||
|
progressPropertyImpl.updateAsync(progress, progressUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
private val messageUpdate = AtomicReference<String>()
|
private val messageUpdate = AtomicReference<String>()
|
||||||
private val messagePropertyImpl = ReadOnlyStringWrapper(this, "message", null)
|
private val messagePropertyImpl = ReadOnlyStringWrapper(this, "message", null)
|
||||||
val messageProperty: ReadOnlyStringProperty = messagePropertyImpl.readOnlyProperty
|
val messageProperty: ReadOnlyStringProperty = messagePropertyImpl.readOnlyProperty
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ class TaskExecutor() {
|
|||||||
* Start the subscription and run all registered tasks asynchronously.
|
* Start the subscription and run all registered tasks asynchronously.
|
||||||
*/
|
*/
|
||||||
fun start() {
|
fun start() {
|
||||||
workerQueue.add(Scheduler.Schedulers.NEW_THREAD.schedule(Callable {
|
workerQueue.add(Scheduler.NEW_THREAD.schedule {
|
||||||
totTask.addAndGet(taskQueue.size)
|
totTask.addAndGet(taskQueue.size)
|
||||||
while (!taskQueue.isEmpty()) {
|
while (!taskQueue.isEmpty()) {
|
||||||
if (canceled) break
|
if (canceled) break
|
||||||
val task = taskQueue.poll()
|
val task = taskQueue.poll()
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
val future = task.scheduler.schedule(Callable { executeTask(task); Unit })
|
val future = task.scheduler.schedule { executeTask(task) }
|
||||||
try {
|
try {
|
||||||
future?.get()
|
future?.get()
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
@@ -66,7 +66,9 @@ class TaskExecutor() {
|
|||||||
}
|
}
|
||||||
if (canceled || Thread.interrupted())
|
if (canceled || Thread.interrupted())
|
||||||
taskListener?.onTerminate()
|
taskListener?.onTerminate()
|
||||||
}))
|
else
|
||||||
|
taskListener?.end()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ package org.jackhuang.hmcl.task
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface TaskListener : EventListener {
|
interface TaskListener : EventListener {
|
||||||
fun onReady(task: Task)
|
fun onReady(task: Task) {}
|
||||||
fun onFinished(task: Task)
|
fun onFinished(task: Task) {}
|
||||||
fun onFailed(task: Task)
|
fun onFailed(task: Task) {}
|
||||||
fun onTerminate()
|
fun onTerminate() {}
|
||||||
|
fun end() {}
|
||||||
}
|
}
|
||||||
@@ -26,14 +26,14 @@ import java.util.logging.*
|
|||||||
import java.util.logging.Formatter
|
import java.util.logging.Formatter
|
||||||
|
|
||||||
val LOG = Logger.getLogger("HMCL").apply {
|
val LOG = Logger.getLogger("HMCL").apply {
|
||||||
level = Level.FINEST
|
level = Level.FINER
|
||||||
useParentHandlers = false
|
useParentHandlers = false
|
||||||
addHandler(FileHandler("hmcl.log").apply {
|
addHandler(FileHandler("hmcl.log").apply {
|
||||||
level = Level.FINEST
|
level = Level.FINER
|
||||||
formatter = DefaultFormatter
|
formatter = DefaultFormatter
|
||||||
})
|
})
|
||||||
addHandler(ConsoleHandler().apply {
|
addHandler(ConsoleHandler().apply {
|
||||||
level = Level.FINEST
|
level = Level.FINER
|
||||||
formatter = DefaultFormatter
|
formatter = DefaultFormatter
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user