Javadoc
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
package org.jackhuang.hmcl
|
package org.jackhuang.hmcl
|
||||||
|
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import java.util.EventObject
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when the selected profile changed.
|
* This event gets fired when the selected profile changed.
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl
|
|||||||
import javafx.application.Application
|
import javafx.application.Application
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import javafx.stage.StageStyle
|
|
||||||
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.ui.Controllers
|
import org.jackhuang.hmcl.ui.Controllers
|
||||||
@@ -29,7 +28,6 @@ import org.jackhuang.hmcl.util.DEFAULT_USER_AGENT
|
|||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import org.jackhuang.hmcl.util.OS
|
import org.jackhuang.hmcl.util.OS
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.Callable
|
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
fun i18n(key: String): String {
|
fun i18n(key: String): String {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.setting.EnumGameDirectory
|
|||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.util.GSON
|
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import org.jackhuang.hmcl.util.fromJson
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|||||||
@@ -29,15 +29,15 @@ import org.jackhuang.hmcl.setting.Settings
|
|||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.task.*
|
import org.jackhuang.hmcl.task.*
|
||||||
import org.jackhuang.hmcl.ui.*
|
import org.jackhuang.hmcl.ui.*
|
||||||
import org.jackhuang.hmcl.util.JavaProcess
|
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel
|
import org.jackhuang.hmcl.util.Log4jLevel
|
||||||
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
|
||||||
object LauncherHelper {
|
object LauncherHelper {
|
||||||
private val launchingStepsPane = LaunchingStepsPane()
|
private val launchingStepsPane = LaunchingStepsPane()
|
||||||
val PROCESS = ConcurrentLinkedQueue<JavaProcess>()
|
val PROCESS = ConcurrentLinkedQueue<ManagedProcess>()
|
||||||
|
|
||||||
fun launch() {
|
fun launch() {
|
||||||
val profile = Settings.selectedProfile
|
val profile = Settings.selectedProfile
|
||||||
@@ -119,11 +119,11 @@ object LauncherHelper {
|
|||||||
authInfo.username to "<player>"
|
authInfo.username to "<player>"
|
||||||
)
|
)
|
||||||
private val launcherVisibility = setting.launcherVisibility
|
private val launcherVisibility = setting.launcherVisibility
|
||||||
private lateinit var process: JavaProcess
|
private lateinit var process: ManagedProcess
|
||||||
private var lwjgl = false
|
private var lwjgl = false
|
||||||
private var logWindow: LogWindow? = null
|
private var logWindow: LogWindow? = null
|
||||||
private val logs = LinkedList<Pair<String, Log4jLevel>>()
|
private val logs = LinkedList<Pair<String, Log4jLevel>>()
|
||||||
override fun setProcess(process: JavaProcess) {
|
override fun setProcess(process: ManagedProcess) {
|
||||||
this.process = process
|
this.process = process
|
||||||
|
|
||||||
if (setting.showLogs) {
|
if (setting.showLogs) {
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ package org.jackhuang.hmcl.setting
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.util.JavaVersion
|
import org.jackhuang.hmcl.util.JavaVersion
|
||||||
import java.io.File
|
import java.util.*
|
||||||
import java.util.TreeMap
|
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
@SerializedName("last")
|
@SerializedName("last")
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import javafx.beans.InvalidationListener
|
|||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.ImmediateObjectProperty
|
||||||
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
import org.jackhuang.hmcl.util.ImmediateStringProperty
|
||||||
import org.jackhuang.hmcl.util.property.ImmediateStringProperty
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.setValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
|||||||
@@ -20,26 +20,23 @@ package org.jackhuang.hmcl.setting
|
|||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import javafx.scene.text.Font
|
import javafx.scene.text.Font
|
||||||
import java.io.IOException
|
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||||
import org.jackhuang.hmcl.util.LOG
|
|
||||||
import java.io.File
|
|
||||||
import java.util.logging.Level
|
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
|
||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
|
||||||
import org.jackhuang.hmcl.auth.Account
|
|
||||||
import org.jackhuang.hmcl.util.*
|
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.property.ImmediateStringProperty
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.PasswordAuthentication
|
import java.net.PasswordAuthentication
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
object Settings {
|
object Settings {
|
||||||
val GSON = GsonBuilder()
|
val GSON = GsonBuilder()
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import javafx.beans.InvalidationListener
|
|||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.property.*
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|||||||
@@ -19,15 +19,14 @@ package org.jackhuang.hmcl.ui
|
|||||||
|
|
||||||
import com.jfoenix.controls.*
|
import com.jfoenix.controls.*
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.fxml.FXML
|
|
||||||
import javafx.scene.Node
|
|
||||||
import javafx.scene.control.ScrollPane
|
|
||||||
import javafx.scene.layout.StackPane
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
import javafx.beans.value.ChangeListener
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.Node
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.control.ToggleGroup
|
import javafx.scene.control.ToggleGroup
|
||||||
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||||
@@ -37,6 +36,7 @@ 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
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class AccountsPage() : StackPane(), DecoratorPage {
|
class AccountsPage() : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||||
@@ -50,14 +50,6 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
@FXML lateinit var cboType: JFXComboBox<String>
|
@FXML lateinit var cboType: JFXComboBox<String>
|
||||||
@FXML lateinit var progressBar: JFXProgressBar
|
@FXML lateinit var progressBar: JFXProgressBar
|
||||||
|
|
||||||
val listener = ChangeListener<Account?> { _, _, newValue ->
|
|
||||||
masonryPane.children.forEach {
|
|
||||||
if (it is AccountItem) {
|
|
||||||
it.chkSelected.isSelected = newValue?.username == it.lblUser.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/account.fxml")
|
loadFXML("/assets/fxml/account.fxml")
|
||||||
children.remove(dialog)
|
children.remove(dialog)
|
||||||
@@ -75,8 +67,8 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
}
|
}
|
||||||
txtPassword.validate()
|
txtPassword.validate()
|
||||||
|
|
||||||
cboType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
cboType.selectionModel.selectedIndexProperty().onChange {
|
||||||
val visible = newValue != 0
|
val visible = it != 0
|
||||||
txtPassword.isVisible = visible
|
txtPassword.isVisible = visible
|
||||||
}
|
}
|
||||||
cboType.selectionModel.select(0)
|
cboType.selectionModel.select(0)
|
||||||
@@ -84,7 +76,13 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
txtPassword.setOnAction { onCreationAccept() }
|
txtPassword.setOnAction { onCreationAccept() }
|
||||||
txtUsername.setOnAction { onCreationAccept() }
|
txtUsername.setOnAction { onCreationAccept() }
|
||||||
|
|
||||||
Settings.selectedAccountProperty.addListener(listener)
|
Settings.selectedAccountProperty.setChangedListener { account ->
|
||||||
|
masonryPane.children.forEach { node ->
|
||||||
|
if (node is AccountItem) {
|
||||||
|
node.chkSelected.isSelected = account?.username == node.lblUser.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadAccounts()
|
loadAccounts()
|
||||||
|
|
||||||
@@ -92,10 +90,6 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
addNewAccount()
|
addNewAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClose() {
|
|
||||||
Settings.selectedAccountProperty.removeListener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadAccounts() {
|
fun loadAccounts() {
|
||||||
val children = mutableListOf<Node>()
|
val children = mutableListOf<Node>()
|
||||||
var i = 0
|
var i = 0
|
||||||
@@ -103,9 +97,9 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
for ((_, account) in Settings.getAccounts()) {
|
for ((_, account) in Settings.getAccounts()) {
|
||||||
children += buildNode(++i, account, group)
|
children += buildNode(++i, account, group)
|
||||||
}
|
}
|
||||||
group.selectedToggleProperty().addListener { _, _, newValue ->
|
group.selectedToggleProperty().onChange {
|
||||||
if (newValue != null)
|
if (it != null)
|
||||||
Settings.selectedAccount = newValue.properties["account"] as Account
|
Settings.selectedAccount = it.properties["account"] as Account
|
||||||
}
|
}
|
||||||
masonryPane.resetChildren(children)
|
masonryPane.resetChildren(children)
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ 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
|
||||||
import com.jfoenix.svg.SVGGlyph
|
import com.jfoenix.svg.SVGGlyph
|
||||||
import javafx.animation.*
|
import javafx.animation.Timeline
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.beans.property.BooleanProperty
|
import javafx.beans.property.BooleanProperty
|
||||||
import javafx.beans.property.ObjectProperty
|
import javafx.beans.property.ObjectProperty
|
||||||
@@ -36,7 +36,6 @@ import javafx.geometry.Insets
|
|||||||
import javafx.scene.Cursor
|
import javafx.scene.Cursor
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.ScrollPane
|
|
||||||
import javafx.scene.control.Tooltip
|
import javafx.scene.control.Tooltip
|
||||||
import javafx.scene.input.MouseEvent
|
import javafx.scene.input.MouseEvent
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.*
|
||||||
@@ -49,7 +48,8 @@ import org.jackhuang.hmcl.ui.animation.AnimationProducer
|
|||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
||||||
import org.jackhuang.hmcl.ui.wizard.*
|
import org.jackhuang.hmcl.ui.wizard.*
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.setValue
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,11 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXComboBox
|
import com.jfoenix.controls.JFXComboBox
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.VBox
|
||||||
import org.jackhuang.hmcl.auth.Account
|
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||||
import javax.swing.event.ChangeListener
|
|
||||||
|
|
||||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||||
val versionsPane = VBox()
|
val versionsPane = VBox()
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.i18n
|
|||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel
|
import org.jackhuang.hmcl.util.Log4jLevel
|
||||||
import org.jackhuang.hmcl.util.inc
|
import org.jackhuang.hmcl.util.inc
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
import org.jackhuang.hmcl.util.readFullyAsString
|
import org.jackhuang.hmcl.util.readFullyAsString
|
||||||
import org.w3c.dom.Document
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
@@ -90,8 +91,8 @@ class LogWindow : Stage() {
|
|||||||
|
|
||||||
engine = webView.engine
|
engine = webView.engine
|
||||||
engine.loadContent(javaClass.getResourceAsStream("/assets/log-window-content.html").readFullyAsString().replace("\${FONT}", "${Settings.font.size}px \"${Settings.font.family}\""))
|
engine.loadContent(javaClass.getResourceAsStream("/assets/log-window-content.html").readFullyAsString().replace("\${FONT}", "${Settings.font.size}px \"${Settings.font.family}\""))
|
||||||
engine.loadWorker.stateProperty().addListener { _, _, newValue ->
|
engine.loadWorker.stateProperty().onChange {
|
||||||
if (newValue == Worker.State.SUCCEEDED) {
|
if (it == Worker.State.SUCCEEDED) {
|
||||||
document = engine.document
|
document = engine.document
|
||||||
body = document.getElementsByTagName("body").item(0)
|
body = document.getElementsByTagName("body").item(0)
|
||||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||||
@@ -105,8 +106,8 @@ class LogWindow : Stage() {
|
|||||||
flag = true
|
flag = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cboLines.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
|
cboLines.selectionModel.selectedItemProperty().onChange {
|
||||||
Settings.logLines = newValue.toInt()
|
Settings.logLines = it?.toInt() ?: 100
|
||||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||||
}
|
}
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import org.jackhuang.hmcl.setting.Settings
|
|||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see /assets/fxml/main.fxml
|
* @see /assets/fxml/main.fxml
|
||||||
@@ -104,9 +105,9 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
profile.repository.getVersions().forEach { version ->
|
profile.repository.getVersions().forEach { version ->
|
||||||
children += buildNode(++i, profile, version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown", group)
|
children += buildNode(++i, profile, version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown", group)
|
||||||
}
|
}
|
||||||
group.selectedToggleProperty().addListener { _, _, newValue ->
|
group.selectedToggleProperty().onChange {
|
||||||
if (newValue != null)
|
if (it != null)
|
||||||
profile.selectedVersion = newValue.properties["version"] as String
|
profile.selectedVersion = it.properties["version"] as String
|
||||||
}
|
}
|
||||||
masonryPane.resetChildren(children)
|
masonryPane.resetChildren(children)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.i18n
|
|||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
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.util.onChange
|
||||||
|
|
||||||
class ModController {
|
class ModController {
|
||||||
@FXML lateinit var scrollPane: ScrollPane
|
@FXML lateinit var scrollPane: ScrollPane
|
||||||
@@ -71,8 +72,8 @@ class ModController {
|
|||||||
JFXDepthManager.setDepth(this, 1)
|
JFXDepthManager.setDepth(this, 1)
|
||||||
style += "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
style += "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
||||||
|
|
||||||
modInfo.activeProperty.addListener { _, _, newValue ->
|
modInfo.activeProperty.onChange {
|
||||||
if (newValue)
|
if (it)
|
||||||
styleClass -= "disabled"
|
styleClass -= "disabled"
|
||||||
else
|
else
|
||||||
styleClass += "disabled"
|
styleClass += "disabled"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import javafx.fxml.FXML
|
|||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.BorderPane
|
import javafx.scene.layout.BorderPane
|
||||||
import org.jackhuang.hmcl.mod.ModInfo
|
import org.jackhuang.hmcl.mod.ModInfo
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : BorderPane() {
|
class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : BorderPane() {
|
||||||
@FXML lateinit var lblModFileName: Label
|
@FXML lateinit var lblModFileName: Label
|
||||||
@@ -34,8 +35,8 @@ class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : Bo
|
|||||||
lblModFileName.text = info.fileName
|
lblModFileName.text = info.fileName
|
||||||
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.mcversion}, Authors: ${info.authors}"
|
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.mcversion}, Authors: ${info.authors}"
|
||||||
chkEnabled.isSelected = info.isActive
|
chkEnabled.isSelected = info.isActive
|
||||||
chkEnabled.selectedProperty().addListener { _, _, newValue ->
|
chkEnabled.selectedProperty().onChange {
|
||||||
info.activeProperty.set(newValue)
|
info.activeProperty.set(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import javafx.fxml.FXMLLoader
|
|||||||
import javafx.scene.Group
|
import javafx.scene.Group
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.shape.SVGPath
|
import javafx.scene.shape.SVGPath
|
||||||
import kotlin.coroutines.experimental.EmptyCoroutineContext.plus
|
|
||||||
|
|
||||||
object SVG {
|
object SVG {
|
||||||
val svgNames = setOf("gear")
|
val svgNames = setOf("gear")
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.ui.construct.FileItem
|
|||||||
import org.jackhuang.hmcl.ui.construct.FontComboBox
|
import org.jackhuang.hmcl.ui.construct.FontComboBox
|
||||||
import org.jackhuang.hmcl.ui.construct.Validator
|
import org.jackhuang.hmcl.ui.construct.Validator
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class SettingsPage : StackPane(), DecoratorPage {
|
class SettingsPage : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.launcher"))
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.launcher"))
|
||||||
@@ -59,41 +60,33 @@ class SettingsPage : StackPane(), DecoratorPage {
|
|||||||
cboDownloadSource.limitWidth(400.0)
|
cboDownloadSource.limitWidth(400.0)
|
||||||
|
|
||||||
txtProxyHost.text = Settings.proxyHost
|
txtProxyHost.text = Settings.proxyHost
|
||||||
txtProxyHost.textProperty().addListener { _, _, newValue ->
|
txtProxyHost.textProperty().onChange { Settings.proxyHost = it }
|
||||||
Settings.proxyHost = newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
txtProxyPort.text = Settings.proxyPort
|
txtProxyPort.text = Settings.proxyPort
|
||||||
txtProxyPort.textProperty().addListener { _, _, newValue ->
|
txtProxyPort.textProperty().onChange { Settings.proxyPort = it }
|
||||||
Settings.proxyPort = newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
txtProxyUsername.text = Settings.proxyUser
|
txtProxyUsername.text = Settings.proxyUser
|
||||||
txtProxyUsername.textProperty().addListener { _, _, newValue ->
|
txtProxyUsername.textProperty().onChange { Settings.proxyUser = it }
|
||||||
Settings.proxyUser = newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
txtProxyPassword.text = Settings.proxyPass
|
txtProxyPassword.text = Settings.proxyPass
|
||||||
txtProxyPassword.textProperty().addListener { _, _, newValue ->
|
txtProxyPassword.textProperty().onChange { Settings.proxyPass = it }
|
||||||
Settings.proxyPass = newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
cboDownloadSource.selectionModel.select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.downloadProvider))
|
cboDownloadSource.selectionModel.select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.downloadProvider))
|
||||||
cboDownloadSource.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
cboDownloadSource.selectionModel.selectedIndexProperty().onChange {
|
||||||
Settings.downloadProvider = DownloadProviders.getDownloadProvider(newValue.toInt())
|
Settings.downloadProvider = DownloadProviders.getDownloadProvider(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
cboFont.selectionModel.select(Settings.font.family)
|
cboFont.selectionModel.select(Settings.font.family)
|
||||||
cboFont.valueProperty().addListener { _, _, newValue ->
|
cboFont.valueProperty().onChange {
|
||||||
val font = Font.font(newValue, Settings.font.size)
|
val font = Font.font(it, Settings.font.size)
|
||||||
Settings.font = font
|
Settings.font = font
|
||||||
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${font.family}\";"
|
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${font.family}\";"
|
||||||
}
|
}
|
||||||
txtFontSize.text = Settings.font.size.toString()
|
txtFontSize.text = Settings.font.size.toString()
|
||||||
txtFontSize.validators += Validator { it.toDoubleOrNull() != null }
|
txtFontSize.validators += Validator { it.toDoubleOrNull() != null }
|
||||||
txtFontSize.textProperty().addListener { _, _, newValue ->
|
txtFontSize.textProperty().onChange {
|
||||||
if (txtFontSize.validate()) {
|
if (txtFontSize.validate()) {
|
||||||
val font = Font.font(Settings.font.family, newValue.toDouble())
|
val font = Font.font(Settings.font.family, it!!.toDouble())
|
||||||
Settings.font = font
|
Settings.font = font
|
||||||
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.font.family}\";"
|
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.font.family}\";"
|
||||||
}
|
}
|
||||||
@@ -106,13 +99,13 @@ class SettingsPage : StackPane(), DecoratorPage {
|
|||||||
}
|
}
|
||||||
cboLanguage.items = list
|
cboLanguage.items = list
|
||||||
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.locale))
|
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.locale))
|
||||||
cboLanguage.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
cboLanguage.selectionModel.selectedIndexProperty().onChange {
|
||||||
Settings.locale = Locales.getLocale(newValue.toInt())
|
Settings.locale = Locales.getLocale(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
cboProxyType.selectionModel.select(Proxies.PROXIES.indexOf(Settings.proxyType))
|
cboProxyType.selectionModel.select(Proxies.PROXIES.indexOf(Settings.proxyType))
|
||||||
cboProxyType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
cboProxyType.selectionModel.selectedIndexProperty().onChange {
|
||||||
Settings.proxyType = Proxies.getProxyType(newValue.toInt())
|
Settings.proxyType = Proxies.getProxyType(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileCommonLocation.setProperty(Settings.commonPathProperty)
|
fileCommonLocation.setProperty(Settings.commonPathProperty)
|
||||||
|
|||||||
@@ -18,9 +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.JFXCheckBox
|
|
||||||
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.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
|
|||||||
@@ -20,10 +20,7 @@ package org.jackhuang.hmcl.ui
|
|||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.Button
|
import javafx.scene.control.Button
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.Tooltip
|
|
||||||
import javafx.scene.layout.BorderPane
|
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
|
||||||
|
|
||||||
class VersionListItem(val versionName: String, val gameVersion: String) : StackPane() {
|
class VersionListItem(val versionName: String, val gameVersion: String) : StackPane() {
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.*
|
import com.jfoenix.controls.JFXButton
|
||||||
|
import com.jfoenix.controls.JFXListView
|
||||||
|
import com.jfoenix.controls.JFXPopup
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.Alert
|
import javafx.scene.control.Alert
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.download.GameAssetIndexDownloadTask
|
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ package org.jackhuang.hmcl.ui
|
|||||||
|
|
||||||
import com.jfoenix.controls.*
|
import com.jfoenix.controls.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import javafx.beans.binding.Bindings
|
|
||||||
import javafx.beans.value.ChangeListener
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.geometry.Pos
|
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.control.Toggle
|
import javafx.scene.control.Toggle
|
||||||
import javafx.scene.control.ToggleGroup
|
import javafx.scene.control.ToggleGroup
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.BorderPane
|
||||||
|
import javafx.scene.layout.Pane
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
import javafx.stage.DirectoryChooser
|
import javafx.stage.DirectoryChooser
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ import javafx.scene.SnapshotParameters
|
|||||||
import javafx.scene.image.ImageView
|
import javafx.scene.image.ImageView
|
||||||
import javafx.scene.image.WritableImage
|
import javafx.scene.image.WritableImage
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.shape.Rectangle
|
|
||||||
import javafx.util.Duration
|
import javafx.util.Duration
|
||||||
import org.jackhuang.hmcl.ui.setOverflowHidden
|
|
||||||
import org.jackhuang.hmcl.ui.takeSnapshot
|
import org.jackhuang.hmcl.ui.takeSnapshot
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.construct
|
|||||||
|
|
||||||
import javafx.beans.DefaultProperty
|
import javafx.beans.DefaultProperty
|
||||||
import javafx.beans.property.SimpleIntegerProperty
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.collections.ListChangeListener
|
import javafx.collections.ListChangeListener
|
||||||
@@ -27,7 +26,10 @@ import javafx.collections.ObservableList
|
|||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.setValue
|
||||||
|
import kotlin.collections.plusAssign
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
@DefaultProperty("content")
|
@DefaultProperty("content")
|
||||||
class ComponentList: StackPane() {
|
class ComponentList: StackPane() {
|
||||||
|
|||||||
@@ -18,14 +18,14 @@
|
|||||||
package org.jackhuang.hmcl.ui.construct
|
package org.jackhuang.hmcl.ui.construct
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import com.jfoenix.controls.JFXButton
|
||||||
import javafx.animation.*
|
import javafx.animation.Animation
|
||||||
|
import javafx.animation.KeyFrame
|
||||||
|
import javafx.animation.KeyValue
|
||||||
|
import javafx.animation.Timeline
|
||||||
import javafx.beans.property.SimpleBooleanProperty
|
import javafx.beans.property.SimpleBooleanProperty
|
||||||
import javafx.geometry.Insets
|
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.HBox
|
|
||||||
import javafx.scene.layout.Priority
|
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import javafx.scene.shape.Rectangle
|
import javafx.scene.shape.Rectangle
|
||||||
@@ -33,7 +33,9 @@ import javafx.util.Duration
|
|||||||
import org.jackhuang.hmcl.ui.SINE
|
import org.jackhuang.hmcl.ui.SINE
|
||||||
import org.jackhuang.hmcl.ui.SVG
|
import org.jackhuang.hmcl.ui.SVG
|
||||||
import org.jackhuang.hmcl.ui.limitHeight
|
import org.jackhuang.hmcl.ui.limitHeight
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
import org.jackhuang.hmcl.util.setValue
|
||||||
|
|
||||||
class ComponentListCell(private val content: Node) : StackPane() {
|
class ComponentListCell(private val content: Node) : StackPane() {
|
||||||
|
|
||||||
@@ -146,8 +148,8 @@ class ComponentListCell(private val content: Node) : StackPane() {
|
|||||||
expandAnimation?.play()
|
expandAnimation?.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
expandedProperty.addListener { _, _, newValue ->
|
expandedProperty.onChange {
|
||||||
if (newValue) {
|
if (it) {
|
||||||
expandIcon.rotate = 180.0
|
expandIcon.rotate = 180.0
|
||||||
} else {
|
} else {
|
||||||
expandIcon.rotate = 0.0
|
expandIcon.rotate = 0.0
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ import javafx.scene.layout.VBox
|
|||||||
import javafx.stage.DirectoryChooser
|
import javafx.stage.DirectoryChooser
|
||||||
import org.jackhuang.hmcl.ui.Controllers
|
import org.jackhuang.hmcl.ui.Controllers
|
||||||
import org.jackhuang.hmcl.ui.SVG
|
import org.jackhuang.hmcl.ui.SVG
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.setValue
|
||||||
|
|
||||||
class FileItem : BorderPane() {
|
class FileItem : BorderPane() {
|
||||||
val nameProperty = SimpleStringProperty(this, "name")
|
val nameProperty = SimpleStringProperty(this, "name")
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ import javafx.collections.FXCollections
|
|||||||
import javafx.scene.control.ListCell
|
import javafx.scene.control.ListCell
|
||||||
import javafx.scene.text.Font
|
import javafx.scene.text.Font
|
||||||
import javafx.util.Callback
|
import javafx.util.Callback
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class FontComboBox(@NamedArg("fontSize") fontSize: Double = 12.0, @NamedArg("enableStyle") enableStyle: Boolean = false) : JFXComboBox<String>(FXCollections.observableArrayList(Font.getFamilies())) {
|
class FontComboBox(@NamedArg("fontSize") fontSize: Double = 12.0, @NamedArg("enableStyle") enableStyle: Boolean = false) : JFXComboBox<String>(FXCollections.observableArrayList(Font.getFamilies())) {
|
||||||
init {
|
init {
|
||||||
valueProperty().addListener { _, _, newValue ->
|
valueProperty().onChange {
|
||||||
if (enableStyle)
|
if (enableStyle)
|
||||||
style = "-fx-font-family: \"$newValue\";"
|
style = "-fx-font-family: \"$it\";"
|
||||||
}
|
}
|
||||||
cellFactory = Callback {
|
cellFactory = Callback {
|
||||||
object : ListCell<String>() {
|
object : ListCell<String>() {
|
||||||
|
|||||||
@@ -26,11 +26,15 @@ import javafx.beans.property.SimpleBooleanProperty
|
|||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.geometry.Insets
|
import javafx.geometry.Insets
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.Background
|
||||||
|
import javafx.scene.layout.BackgroundFill
|
||||||
|
import javafx.scene.layout.CornerRadii
|
||||||
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import javafx.scene.paint.Paint
|
import javafx.scene.paint.Paint
|
||||||
import javafx.scene.shape.Rectangle
|
import javafx.scene.shape.Rectangle
|
||||||
import org.jackhuang.hmcl.util.getValue
|
import org.jackhuang.hmcl.util.getValue
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
import org.jackhuang.hmcl.util.setValue
|
import org.jackhuang.hmcl.util.setValue
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
@@ -92,8 +96,8 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
focusedProperty().addListener { _, _, newVal ->
|
focusedProperty().onChange {
|
||||||
if (newVal) {
|
if (it) {
|
||||||
if (!isPressed) {
|
if (!isPressed) {
|
||||||
this.buttonRippler.showOverlay()
|
this.buttonRippler.showOverlay()
|
||||||
}
|
}
|
||||||
@@ -125,7 +129,7 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
|||||||
return@Callable background
|
return@Callable background
|
||||||
}
|
}
|
||||||
}, backgroundProperty()))
|
}, backgroundProperty()))
|
||||||
ripplerFillProperty.addListener { _, _, newVal -> this.buttonRippler.ripplerFill = newVal }
|
ripplerFillProperty.onChange { this.buttonRippler.ripplerFill = it }
|
||||||
if (background == null || this.isJavaDefaultBackground(background)) {
|
if (background == null || this.isJavaDefaultBackground(background)) {
|
||||||
background = Background(BackgroundFill(Color.TRANSPARENT, this.defaultRadii, null))
|
background = Background(BackgroundFill(Color.TRANSPARENT, this.defaultRadii, null))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.construct
|
package org.jackhuang.hmcl.ui.construct
|
||||||
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.util.PropertyResourceBundle
|
|
||||||
import java.util.ResourceBundle
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.Locale
|
import java.io.InputStreamReader
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
object UTF8Control : ResourceBundle.Control() {
|
object UTF8Control : ResourceBundle.Control() {
|
||||||
@Throws(IllegalAccessException::class, InstantiationException::class, IOException::class)
|
@Throws(IllegalAccessException::class, InstantiationException::class, IOException::class)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.download.DownloadProvider
|
|||||||
import org.jackhuang.hmcl.ui.loadFXML
|
import org.jackhuang.hmcl.ui.loadFXML
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class InstallersPage(private val controller: WizardController, private val downloadProvider: DownloadProvider): StackPane(), WizardPage {
|
class InstallersPage(private val controller: WizardController, private val downloadProvider: DownloadProvider): StackPane(), WizardPage {
|
||||||
|
|
||||||
@@ -42,9 +43,9 @@ class InstallersPage(private val controller: WizardController, private val downl
|
|||||||
val gameVersion = controller.settings["game"] as String
|
val gameVersion = controller.settings["game"] as String
|
||||||
txtName.text = gameVersion
|
txtName.text = gameVersion
|
||||||
|
|
||||||
list.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
list.selectionModel.selectedIndexProperty().onChange {
|
||||||
controller.settings[INSTALLER_TYPE] = newValue
|
controller.settings[INSTALLER_TYPE] = it
|
||||||
controller.onNext(when (newValue){
|
controller.onNext(when (it){
|
||||||
0 -> VersionsPage(controller, gameVersion, downloadProvider, "forge") { controller.onPrev(false) }
|
0 -> VersionsPage(controller, gameVersion, downloadProvider, "forge") { controller.onPrev(false) }
|
||||||
1 -> VersionsPage(controller, gameVersion, downloadProvider, "liteloader") { controller.onPrev(false) }
|
1 -> VersionsPage(controller, gameVersion, downloadProvider, "liteloader") { controller.onPrev(false) }
|
||||||
2 -> VersionsPage(controller, gameVersion, downloadProvider, "optifine") { controller.onPrev(false) }
|
2 -> VersionsPage(controller, gameVersion, downloadProvider, "optifine") { controller.onPrev(false) }
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import javafx.stage.FileChooser
|
|||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
|
||||||
import org.jackhuang.hmcl.ui.Controllers
|
import org.jackhuang.hmcl.ui.Controllers
|
||||||
import org.jackhuang.hmcl.ui.construct.Validator
|
import org.jackhuang.hmcl.ui.construct.Validator
|
||||||
import org.jackhuang.hmcl.ui.loadFXML
|
import org.jackhuang.hmcl.ui.loadFXML
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import com.jfoenix.controls.JFXListView
|
|||||||
import com.jfoenix.controls.JFXSpinner
|
import com.jfoenix.controls.JFXSpinner
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion
|
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor
|
import org.jackhuang.hmcl.task.TaskExecutor
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||||
@@ -31,6 +31,7 @@ import org.jackhuang.hmcl.ui.loadFXML
|
|||||||
import org.jackhuang.hmcl.ui.wizard.Refreshable
|
import org.jackhuang.hmcl.ui.wizard.Refreshable
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||||
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class VersionsPage(private val controller: WizardController, private val gameVersion: String, private val downloadProvider: DownloadProvider, private val libraryId: String, private val callback: () -> Unit): StackPane(), WizardPage, Refreshable {
|
class VersionsPage(private val controller: WizardController, private val gameVersion: String, private val downloadProvider: DownloadProvider, private val libraryId: String, private val callback: () -> Unit): StackPane(), WizardPage, Refreshable {
|
||||||
|
|
||||||
@@ -44,8 +45,8 @@ class VersionsPage(private val controller: WizardController, private val gameVer
|
|||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/download/versions.fxml")
|
loadFXML("/assets/fxml/download/versions.fxml")
|
||||||
children.setAll(spinner)
|
children.setAll(spinner)
|
||||||
list.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
|
list.selectionModel.selectedItemProperty().onChange {
|
||||||
controller.settings[libraryId] = newValue.remoteVersion.selfVersion
|
controller.settings[libraryId] = it!!.remoteVersion.selfVersion
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
refresh()
|
refresh()
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.download
|
|||||||
|
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.BorderPane
|
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
import org.jackhuang.hmcl.ui.loadFXML
|
import org.jackhuang.hmcl.ui.loadFXML
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui.wizard
|
|||||||
import com.jfoenix.concurrency.JFXUtilities
|
import com.jfoenix.concurrency.JFXUtilities
|
||||||
import com.jfoenix.controls.JFXProgressBar
|
import com.jfoenix.controls.JFXProgressBar
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.scene.Node
|
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<?import com.jfoenix.transitions.hamburger.HamburgerBackArrowBasicTransition?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
@@ -60,7 +59,11 @@
|
|||||||
<BorderPane minWidth="200" maxWidth="200" fx:id="titleWrapper">
|
<BorderPane minWidth="200" maxWidth="200" fx:id="titleWrapper">
|
||||||
<center>
|
<center>
|
||||||
<Label fx:id="lblTitle" BorderPane.alignment="CENTER" mouseTransparent="true"
|
<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;">
|
||||||
|
<BorderPane.margin>
|
||||||
|
<Insets left="3" />
|
||||||
|
</BorderPane.margin>
|
||||||
|
</Label>
|
||||||
</center>
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<Rectangle height="${navBar.height}" width="1" fill="gray"/>
|
<Rectangle height="${navBar.height}" width="1" fill="gray"/>
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.auth.yggdrasil
|
|||||||
|
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import com.google.gson.JsonObject
|
import java.util.*
|
||||||
import java.util.UUID
|
|
||||||
import com.google.gson.JsonParseException
|
|
||||||
|
|
||||||
data class GameProfile(
|
data class GameProfile(
|
||||||
val id: UUID? = null,
|
val id: UUID? = null,
|
||||||
|
|||||||
@@ -20,13 +20,6 @@ package org.jackhuang.hmcl.auth.yggdrasil
|
|||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import com.google.gson.JsonArray
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import kotlin.collections.HashMap
|
|
||||||
import com.google.gson.JsonPrimitive
|
|
||||||
import com.google.gson.JsonSerializationContext
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PropertyMap: HashMap<String, Property>() {
|
class PropertyMap: HashMap<String, Property>() {
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import com.google.gson.GsonBuilder
|
|||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
import org.jackhuang.hmcl.auth.*
|
import org.jackhuang.hmcl.auth.*
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.doGet
|
|
||||||
import org.jackhuang.hmcl.util.doPost
|
|
||||||
import org.jackhuang.hmcl.util.toURL
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
import org.jackhuang.hmcl.game.*
|
import org.jackhuang.hmcl.game.GameRepository
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
|
||||||
abstract class AbstractDependencyManager(repository: GameRepository, proxy: Proxy)
|
abstract class AbstractDependencyManager
|
||||||
: DependencyManager(repository, proxy) {
|
: DependencyManager {
|
||||||
abstract val downloadProvider: DownloadProvider
|
abstract val downloadProvider: DownloadProvider
|
||||||
|
|
||||||
fun getVersions(id: String, selfVersion: String) =
|
fun getVersions(id: String, selfVersion: String) =
|
||||||
|
|||||||
@@ -17,6 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.forge.ForgeVersionList
|
||||||
|
import org.jackhuang.hmcl.download.game.GameVersionList
|
||||||
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList
|
||||||
|
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see {@link http://bmclapi2.bangbang93.com}
|
||||||
|
*/
|
||||||
object BMCLAPIDownloadProvider : DownloadProvider() {
|
object BMCLAPIDownloadProvider : DownloadProvider() {
|
||||||
override val libraryBaseURL: String = "http://bmclapi2.bangbang93.com/libraries/"
|
override val libraryBaseURL: String = "http://bmclapi2.bangbang93.com/libraries/"
|
||||||
override val versionListURL: String = "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json"
|
override val versionListURL: String = "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json"
|
||||||
|
|||||||
@@ -17,6 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.forge.ForgeInstallTask
|
||||||
|
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLoggingDownloadTask
|
||||||
|
import org.jackhuang.hmcl.download.game.VersionJSONSaveTask
|
||||||
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask
|
||||||
|
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask
|
||||||
import org.jackhuang.hmcl.game.DefaultGameRepository
|
import org.jackhuang.hmcl.game.DefaultGameRepository
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
import org.jackhuang.hmcl.task.ParallelTask
|
import org.jackhuang.hmcl.task.ParallelTask
|
||||||
@@ -27,8 +34,8 @@ import java.net.Proxy
|
|||||||
/**
|
/**
|
||||||
* This class has no state.
|
* This class has no state.
|
||||||
*/
|
*/
|
||||||
class DefaultDependencyManager(override val repository: DefaultGameRepository, override var downloadProvider: DownloadProvider, proxy: Proxy = Proxy.NO_PROXY)
|
class DefaultDependencyManager(override val repository: DefaultGameRepository, override var downloadProvider: DownloadProvider, override val proxy: Proxy = Proxy.NO_PROXY)
|
||||||
: AbstractDependencyManager(repository, proxy) {
|
: AbstractDependencyManager() {
|
||||||
|
|
||||||
override fun gameBuilder(): GameBuilder = DefaultGameBuilder(this)
|
override fun gameBuilder(): GameBuilder = DefaultGameBuilder(this)
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
import org.jackhuang.hmcl.game.*
|
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLoggingDownloadTask
|
||||||
|
import org.jackhuang.hmcl.download.game.VersionJSONSaveTask
|
||||||
|
import org.jackhuang.hmcl.game.Version
|
||||||
import org.jackhuang.hmcl.task.*
|
import org.jackhuang.hmcl.task.*
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
|
import org.jackhuang.hmcl.util.GSON
|
||||||
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
|
import org.jackhuang.hmcl.util.toURL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameBuilder() {
|
class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameBuilder() {
|
||||||
|
|||||||
@@ -22,27 +22,51 @@ import org.jackhuang.hmcl.game.Version
|
|||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
|
||||||
abstract class DependencyManager(open val repository: GameRepository, open val proxy: Proxy) {
|
/**
|
||||||
|
* Do everything that will connect to Internet.
|
||||||
|
* Downloading Minecraft files.
|
||||||
|
*/
|
||||||
|
interface DependencyManager {
|
||||||
|
/**
|
||||||
|
* The relied game repository.
|
||||||
|
*/
|
||||||
|
val repository: GameRepository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The proxy that all network operations should go through.
|
||||||
|
*/
|
||||||
|
val proxy: Proxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the game is complete.
|
* Check if the game is complete.
|
||||||
* Check libraries, assets, logging files and so on.
|
* Check libraries, assets, logging files and so on.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the task to check game completion.
|
||||||
*/
|
*/
|
||||||
abstract fun checkGameCompletionAsync(version: Version): Task
|
fun checkGameCompletionAsync(version: Version): Task
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
|
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
|
||||||
*/
|
*/
|
||||||
abstract fun gameBuilder(): GameBuilder
|
fun gameBuilder(): GameBuilder
|
||||||
|
|
||||||
abstract fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
|
/**
|
||||||
|
* Install a library to a version.
|
||||||
|
* **Note**: Installing a library may change the version.json.
|
||||||
|
*
|
||||||
|
* @param gameVersion the Minecraft version that the library relies on.
|
||||||
|
* @param version the version.json.
|
||||||
|
* @param libraryId the type of being installed library. i.e. "forge", "liteloader", "optifine"
|
||||||
|
* @param libraryVersion the version of being installed library.
|
||||||
|
* @return the task to install the specific library.
|
||||||
|
*/
|
||||||
|
fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get registered version list.
|
* Get registered version list.
|
||||||
|
*
|
||||||
* @param id the id of version list. i.e. game, forge, liteloader, optifine
|
* @param id the id of version list. i.e. game, forge, liteloader, optifine
|
||||||
* @throws IllegalArgumentException if the version list of specific id is not found.
|
* @throws IllegalArgumentException if the version list of specific id is not found.
|
||||||
*/
|
*/
|
||||||
abstract fun getVersionList(id: String): VersionList<*>
|
fun getVersionList(id: String): VersionList<*>
|
||||||
}
|
}
|
||||||
@@ -17,14 +17,31 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.VersionList
|
/**
|
||||||
|
* The service provider that provides Minecraft online file downloads.
|
||||||
|
*/
|
||||||
abstract class DownloadProvider {
|
abstract class DownloadProvider {
|
||||||
abstract val libraryBaseURL: String
|
abstract val libraryBaseURL: String
|
||||||
abstract val versionListURL: String
|
abstract val versionListURL: String
|
||||||
abstract val versionBaseURL: String
|
abstract val versionBaseURL: String
|
||||||
abstract val assetIndexBaseURL: String
|
abstract val assetIndexBaseURL: String
|
||||||
abstract val assetBaseURL: String
|
abstract val assetBaseURL: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject into original URL provided by Mojang and Forge.
|
||||||
|
*
|
||||||
|
* Since there are many provided URLs that are written in JSONs and are unmodifiable,
|
||||||
|
* this method provides a way to change them.
|
||||||
|
*
|
||||||
|
* @param baseURL original URL provided by Mojang and Forge.
|
||||||
|
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
||||||
|
*/
|
||||||
abstract fun injectURL(baseURL: String): String
|
abstract fun injectURL(baseURL: String): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||||
|
* @param id the id of specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||||
|
* @return the version list
|
||||||
|
*/
|
||||||
abstract fun getVersionListById(id: String): VersionList<*>
|
abstract fun getVersionListById(id: String): VersionList<*>
|
||||||
}
|
}
|
||||||
@@ -19,12 +19,20 @@ package org.jackhuang.hmcl.download
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The builder which provide a task to build Minecraft environment.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
abstract class GameBuilder {
|
abstract class GameBuilder {
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
protected var gameVersion: String = ""
|
protected var gameVersion: String = ""
|
||||||
protected var toolVersions = HashMap<String, String>()
|
protected var toolVersions = HashMap<String, String>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The new game version name, for .minecraft/<version name>.
|
||||||
|
* @param name the name of new game version.
|
||||||
|
*/
|
||||||
fun name(name: String): GameBuilder {
|
fun name(name: String): GameBuilder {
|
||||||
this.name = name
|
this.name = name
|
||||||
return this
|
return this
|
||||||
@@ -35,10 +43,17 @@ abstract class GameBuilder {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param id the core library id. i.e. "forge", "liteloader", "optifine"
|
||||||
|
* @param version the version of the core library. For documents, you can first try [VersionList.versions]
|
||||||
|
*/
|
||||||
fun version(id: String, version: String): GameBuilder {
|
fun version(id: String, version: String): GameBuilder {
|
||||||
toolVersions[id] = version
|
toolVersions[id] = version
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the task that can build thw whole Minecraft environment
|
||||||
|
*/
|
||||||
abstract fun buildAsync(): Task
|
abstract fun buildAsync(): Task
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.forge.ForgeVersionList
|
||||||
|
import org.jackhuang.hmcl.download.game.GameVersionList
|
||||||
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList
|
||||||
|
import org.jackhuang.hmcl.download.optifine.OptiFineVersionList
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see {@link http://wiki.vg}
|
||||||
|
*/
|
||||||
object MojangDownloadProvider : DownloadProvider() {
|
object MojangDownloadProvider : DownloadProvider() {
|
||||||
override val libraryBaseURL: String = "https://libraries.minecraft.net/"
|
override val libraryBaseURL: String = "https://libraries.minecraft.net/"
|
||||||
override val versionBaseURL: String = "http://s3.amazonaws.com/Minecraft.Download/versions/"
|
override val versionBaseURL: String = "http://s3.amazonaws.com/Minecraft.Download/versions/"
|
||||||
@@ -37,15 +44,6 @@ object MojangDownloadProvider : DownloadProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun injectURL(baseURL: String): String {
|
override fun injectURL(baseURL: String): String {
|
||||||
/**if (baseURL.contains("scala-swing") || baseURL.contains("scala-xml") || baseURL.contains("scala-parser-combinators"))
|
|
||||||
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven/");
|
|
||||||
else if (baseURL.contains("typesafe") || baseURL.contains("scala"))
|
|
||||||
if (Locale.getDefault() == Locale.CHINA)
|
|
||||||
return baseURL.replace("http://files.minecraftforge.net/maven", "http://maven.aliyun.com/nexus/content/groups/public");
|
|
||||||
else
|
|
||||||
return baseURL.replace("http://files.minecraftforge.net/maven", "http://repo1.maven.org/maven2");
|
|
||||||
else
|
|
||||||
return baseURL; */
|
|
||||||
if (baseURL.endsWith("net/minecraftforge/forge/json"))
|
if (baseURL.endsWith("net/minecraftforge/forge/json"))
|
||||||
return baseURL
|
return baseURL
|
||||||
else if (Locale.getDefault() == Locale.CHINA)
|
else if (Locale.getDefault() == Locale.CHINA)
|
||||||
|
|||||||
@@ -19,14 +19,18 @@ package org.jackhuang.hmcl.download
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.util.VersionNumber
|
import org.jackhuang.hmcl.util.VersionNumber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.Comparator
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote version.
|
||||||
|
*
|
||||||
|
* @param gameVersion the Minecraft version that this remote version suits.
|
||||||
|
* @param selfVersion the version string of the remote version.
|
||||||
|
* @param url the installer or universal jar URL.
|
||||||
|
* @param tag some necessary information for Installer Task.
|
||||||
|
*/
|
||||||
data class RemoteVersion<T> (
|
data class RemoteVersion<T> (
|
||||||
val gameVersion: String,
|
val gameVersion: String,
|
||||||
val selfVersion: String,
|
val selfVersion: String,
|
||||||
/**
|
|
||||||
* The file of remote version, may be an installer or an universal jar.
|
|
||||||
*/
|
|
||||||
val url: String,
|
val url: String,
|
||||||
val tag: T
|
val tag: T
|
||||||
): Comparable<RemoteVersion<T>> {
|
): Comparable<RemoteVersion<T>> {
|
||||||
@@ -39,6 +43,7 @@ data class RemoteVersion<T> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun compareTo(other: RemoteVersion<T>): Int {
|
override fun compareTo(other: RemoteVersion<T>): Int {
|
||||||
|
// newer versions are smaller than older versions
|
||||||
return -selfVersion.compareTo(other.selfVersion)
|
return -selfVersion.compareTo(other.selfVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,20 +22,37 @@ import org.jackhuang.hmcl.util.SimpleMultimap
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote version list.
|
||||||
|
* @param T The type of RemoteVersion<T>, the type of tags.
|
||||||
|
*/
|
||||||
abstract class VersionList<T> {
|
abstract class VersionList<T> {
|
||||||
@JvmField
|
/**
|
||||||
|
* the remote version list.
|
||||||
|
* key: game version.
|
||||||
|
* values: corresponding remote versions.
|
||||||
|
*/
|
||||||
protected val versions = SimpleMultimap<String, RemoteVersion<T>>(::HashMap, ::TreeSet)
|
protected val versions = SimpleMultimap<String, RemoteVersion<T>>(::HashMap, ::TreeSet)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the version list has been loaded.
|
||||||
|
*/
|
||||||
val loaded = versions.isNotEmpty
|
val loaded = versions.isNotEmpty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param downloadProvider DownloadProvider
|
||||||
|
* @return the task to reload the remote version list.
|
||||||
|
*/
|
||||||
abstract fun refreshAsync(downloadProvider: DownloadProvider): Task
|
abstract fun refreshAsync(downloadProvider: DownloadProvider): Task
|
||||||
|
|
||||||
protected open fun getVersionsImpl(gameVersion: String): Collection<RemoteVersion<T>> {
|
private fun getVersionsImpl(gameVersion: String): Collection<RemoteVersion<T>> {
|
||||||
val ans = versions[gameVersion]
|
val ans = versions[gameVersion]
|
||||||
return if (ans.isEmpty()) versions.values else ans
|
return if (ans.isEmpty()) versions.values else ans
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the remote versions that specifics Minecraft version.
|
||||||
|
*
|
||||||
* @param gameVersion the Minecraft version that remote versions belong to
|
* @param gameVersion the Minecraft version that remote versions belong to
|
||||||
* @return the collection of specific remote versions
|
* @return the collection of specific remote versions
|
||||||
*/
|
*/
|
||||||
@@ -43,6 +60,13 @@ abstract class VersionList<T> {
|
|||||||
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion))
|
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specific remote version.
|
||||||
|
*
|
||||||
|
* @param gameVersion the Minecraft version that remote versions belong to
|
||||||
|
* @param remoteVersion the version of the remote version.
|
||||||
|
* @return the specific remote version, null if it is not found.
|
||||||
|
*/
|
||||||
fun getVersion(gameVersion: String, remoteVersion: String): RemoteVersion<T>? {
|
fun getVersion(gameVersion: String, remoteVersion: String): RemoteVersion<T>? {
|
||||||
var result : RemoteVersion<T>? = null
|
var result : RemoteVersion<T>? = null
|
||||||
versions[gameVersion].forEach {
|
versions[gameVersion].forEach {
|
||||||
|
|||||||
@@ -15,8 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.forge
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask
|
||||||
import org.jackhuang.hmcl.game.Library
|
import org.jackhuang.hmcl.game.Library
|
||||||
import org.jackhuang.hmcl.game.SimpleVersionProvider
|
import org.jackhuang.hmcl.game.SimpleVersionProvider
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.forge
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
@@ -15,8 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.forge
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.VersionList
|
||||||
import org.jackhuang.hmcl.task.GetTask
|
import org.jackhuang.hmcl.task.GetTask
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
@@ -15,8 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.game
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.AbstractDependencyManager
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
|
import org.jackhuang.hmcl.download.DependencyManager
|
||||||
import org.jackhuang.hmcl.game.AssetIndex
|
import org.jackhuang.hmcl.game.AssetIndex
|
||||||
import org.jackhuang.hmcl.game.AssetObject
|
import org.jackhuang.hmcl.game.AssetObject
|
||||||
import org.jackhuang.hmcl.game.DownloadType
|
import org.jackhuang.hmcl.game.DownloadType
|
||||||
@@ -30,13 +33,12 @@ import java.io.IOException
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This task is to download game libraries.
|
* This task is to download game libraries.
|
||||||
* This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task)
|
* This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task)
|
||||||
* @param resolvedVersion the <b>resolved</b> version
|
* @param resolvedVersion the <b>resolved</b> version
|
||||||
*/
|
*/
|
||||||
class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager, private val resolvedVersion: Version): Task() {
|
class GameLibrariesTask(private val dependencyManager: AbstractDependencyManager, private val resolvedVersion: Version): Task() {
|
||||||
override val dependencies = LinkedList<Task>()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
for (library in resolvedVersion.libraries)
|
for (library in resolvedVersion.libraries)
|
||||||
@@ -49,7 +51,12 @@ class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
/**
|
||||||
|
* This task is to download log4j configuration file provided in minecraft.json.
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and [GameRepository]
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
|
class GameLoggingDownloadTask(private val dependencyManager: DependencyManager, private val version: Version) : Task() {
|
||||||
override val dependencies = LinkedList<Task>()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val logging = version.logging?.get(DownloadType.CLIENT) ?: return
|
val logging = version.logging?.get(DownloadType.CLIENT) ?: return
|
||||||
@@ -59,6 +66,11 @@ class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyMa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to download asset index file provided in minecraft.json.
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and [GameRepository]
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
||||||
override val dependencies = LinkedList<Task>()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
@@ -72,6 +84,11 @@ class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to extract all asset objects described in asset index json.
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and [GameRepository]
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : TaskResult<Collection<Pair<File, AssetObject>>>() {
|
class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : TaskResult<Collection<Pair<File, AssetObject>>>() {
|
||||||
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
|
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
|
||||||
private val assetIndexInfo = version.actualAssetIndex
|
private val assetIndexInfo = version.actualAssetIndex
|
||||||
@@ -100,6 +117,11 @@ class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to download all asset objects provided in asset index json.
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and [GameRepository]
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
||||||
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
|
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
|
||||||
override val dependents = listOf(refreshTask)
|
override val dependents = listOf(refreshTask)
|
||||||
@@ -140,6 +162,11 @@ class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to save the version json.
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and [GameRepository]
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
class VersionJSONSaveTask(private val dependencyManager: DefaultDependencyManager, private val version: Version): Task() {
|
class VersionJSONSaveTask(private val dependencyManager: DefaultDependencyManager, private val version: Version): Task() {
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val json = dependencyManager.repository.getVersionJson(version.id).absoluteFile
|
val json = dependencyManager.repository.getVersionJson(version.id).absoluteFile
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.game
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import org.jackhuang.hmcl.game.ReleaseType
|
import org.jackhuang.hmcl.game.ReleaseType
|
||||||
@@ -15,13 +15,16 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.game
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.VersionList
|
||||||
|
import org.jackhuang.hmcl.task.GetTask
|
||||||
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.GSON
|
import org.jackhuang.hmcl.util.GSON
|
||||||
import org.jackhuang.hmcl.util.asVersion
|
import org.jackhuang.hmcl.util.asVersion
|
||||||
import org.jackhuang.hmcl.util.fromJson
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
import org.jackhuang.hmcl.task.GetTask
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import org.jackhuang.hmcl.util.toURL
|
import org.jackhuang.hmcl.util.toURL
|
||||||
|
|
||||||
|
|
||||||
@@ -15,8 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.liteloader
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask
|
||||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo
|
||||||
import org.jackhuang.hmcl.game.Library
|
import org.jackhuang.hmcl.game.Library
|
||||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
||||||
@@ -27,7 +30,7 @@ import org.jackhuang.hmcl.task.then
|
|||||||
import org.jackhuang.hmcl.util.merge
|
import org.jackhuang.hmcl.util.merge
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LiteLoader must be installed after Forge.
|
* Note: LiteLoader must be installed after Forge.
|
||||||
*/
|
*/
|
||||||
class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyManager,
|
class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||||
private val gameVersion: String,
|
private val gameVersion: String,
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.liteloader
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import org.jackhuang.hmcl.game.Library
|
import org.jackhuang.hmcl.game.Library
|
||||||
@@ -15,13 +15,16 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.liteloader
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.VersionList
|
||||||
|
import org.jackhuang.hmcl.task.GetTask
|
||||||
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.GSON
|
import org.jackhuang.hmcl.util.GSON
|
||||||
import org.jackhuang.hmcl.util.asVersion
|
import org.jackhuang.hmcl.util.asVersion
|
||||||
import org.jackhuang.hmcl.util.fromJson
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
import org.jackhuang.hmcl.task.GetTask
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import org.jackhuang.hmcl.util.toURL
|
import org.jackhuang.hmcl.util.toURL
|
||||||
|
|
||||||
object LiteLoaderVersionList : VersionList<LiteLoaderRemoteVersionTag>() {
|
object LiteLoaderVersionList : VersionList<LiteLoaderRemoteVersionTag>() {
|
||||||
@@ -15,15 +15,17 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.optifine
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.VersionList
|
||||||
import org.jackhuang.hmcl.task.GetTask
|
import org.jackhuang.hmcl.task.GetTask
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.GSON
|
import org.jackhuang.hmcl.util.GSON
|
||||||
import org.jackhuang.hmcl.util.asVersion
|
import org.jackhuang.hmcl.util.asVersion
|
||||||
import org.jackhuang.hmcl.util.toURL
|
import org.jackhuang.hmcl.util.toURL
|
||||||
import org.jackhuang.hmcl.util.typeOf
|
import org.jackhuang.hmcl.util.typeOf
|
||||||
import java.util.TreeSet
|
|
||||||
|
|
||||||
object OptiFineBMCLVersionList : VersionList<Unit>() {
|
object OptiFineBMCLVersionList : VersionList<Unit>() {
|
||||||
override fun refreshAsync(downloadProvider: DownloadProvider): Task {
|
override fun refreshAsync(downloadProvider: DownloadProvider): Task {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.optifine
|
||||||
|
|
||||||
|
|
||||||
object Opt
|
object Opt
|
||||||
@@ -15,8 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.optifine
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask
|
||||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo
|
||||||
import org.jackhuang.hmcl.game.Library
|
import org.jackhuang.hmcl.game.Library
|
||||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
||||||
@@ -26,6 +29,9 @@ import org.jackhuang.hmcl.task.TaskResult
|
|||||||
import org.jackhuang.hmcl.task.then
|
import org.jackhuang.hmcl.task.then
|
||||||
import org.jackhuang.hmcl.util.merge
|
import org.jackhuang.hmcl.util.merge
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **Note**: OptiFine should be installed in the end.
|
||||||
|
*/
|
||||||
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||||
private val gameVersion: String,
|
private val gameVersion: String,
|
||||||
private val version: Version,
|
private val version: Version,
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.optifine
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
@@ -15,15 +15,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download.optifine
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
|
import org.jackhuang.hmcl.download.VersionList
|
||||||
import org.jackhuang.hmcl.task.GetTask
|
import org.jackhuang.hmcl.task.GetTask
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.toURL
|
import org.jackhuang.hmcl.util.toURL
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
|
||||||
object OptiFineVersionList : VersionList<Unit>() {
|
object OptiFineVersionList : VersionList<Unit>() {
|
||||||
private val pattern = Pattern.compile("OptiFine (.*?) ")
|
private val pattern = Pattern.compile("OptiFine (.*?) ")
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.event
|
package org.jackhuang.hmcl.event
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.JavaProcess
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
import java.util.EventObject
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when loading versions in a .minecraft folder.
|
* This event gets fired when loading versions in a .minecraft folder.
|
||||||
@@ -59,7 +59,7 @@ class LoadedOneVersionEvent(source: Any, val version: String) : EventObject(sour
|
|||||||
* @param JavaProcess The process that exited abnormally.
|
* @param JavaProcess The process that exited abnormally.
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
class JavaProcessExitedAbnormallyEvent(source: Any, val value: JavaProcess) : EventObject(source)
|
class JavaProcessExitedAbnormallyEvent(source: Any, val value: ManagedProcess) : EventObject(source)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when minecraft process exited successfully and the exit code is 0.
|
* This event gets fired when minecraft process exited successfully and the exit code is 0.
|
||||||
@@ -69,7 +69,7 @@ class JavaProcessExitedAbnormallyEvent(source: Any, val value: JavaProcess) : Ev
|
|||||||
* @param JavaProcess minecraft process
|
* @param JavaProcess minecraft process
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
class JavaProcessStoppedEvent(source: Any, val value: JavaProcess) : EventObject(source)
|
class JavaProcessStoppedEvent(source: Any, val value: ManagedProcess) : EventObject(source)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when we launch the JVM and it got crashed.
|
* This event gets fired when we launch the JVM and it got crashed.
|
||||||
@@ -79,4 +79,4 @@ class JavaProcessStoppedEvent(source: Any, val value: JavaProcess) : EventObject
|
|||||||
* @param JavaProcess the crashed process.
|
* @param JavaProcess the crashed process.
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
class JVMLaunchFailedEvent(source: Any, val value: JavaProcess) : EventObject(source)
|
class JVMLaunchFailedEvent(source: Any, val value: ManagedProcess) : EventObject(source)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import jdk.nashorn.internal.ir.annotations.Immutable
|
import jdk.nashorn.internal.ir.annotations.Immutable
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class AssetIndexInfo(
|
class AssetIndexInfo(
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ package org.jackhuang.hmcl.game
|
|||||||
import org.jackhuang.hmcl.util.Immutable
|
import org.jackhuang.hmcl.util.Immutable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Minecraft version for 1.5.x and earlier.
|
||||||
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
class ClassicVersion : Version(
|
class ClassicVersion : Version(
|
||||||
mainClass = "net.minecraft.client.Minecraft",
|
mainClass = "net.minecraft.client.Minecraft",
|
||||||
@@ -41,6 +44,9 @@ class ClassicVersion : Version(
|
|||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* Check if this directory is an old style Minecraft repository.
|
||||||
|
*/
|
||||||
fun hasClassicVersion(baseDirectory: File): Boolean {
|
fun hasClassicVersion(baseDirectory: File): Boolean {
|
||||||
val file = File(baseDirectory, "bin")
|
val file = File(baseDirectory, "bin")
|
||||||
if (!file.exists()) return false
|
if (!file.exists()) return false
|
||||||
|
|||||||
@@ -18,7 +18,10 @@
|
|||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import org.jackhuang.hmcl.event.*
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
|
import org.jackhuang.hmcl.event.LoadedOneVersionEvent
|
||||||
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
|
import org.jackhuang.hmcl.event.RefreshingVersionsEvent
|
||||||
import org.jackhuang.hmcl.util.GSON
|
import org.jackhuang.hmcl.util.GSON
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import org.jackhuang.hmcl.util.fromJson
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
@@ -28,6 +31,9 @@ import java.io.IOException
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of classic Minecraft game repository.
|
||||||
|
*/
|
||||||
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||||
protected val versions: MutableMap<String, Version> = TreeMap<String, Version>()
|
protected val versions: MutableMap<String, Version> = TreeMap<String, Version>()
|
||||||
|
|
||||||
@@ -44,6 +50,11 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
val id = v.jar ?: v.id
|
val id = v.jar ?: v.id
|
||||||
return getVersionRoot(id).resolve("$id.jar")
|
return getVersionRoot(id).resolve("$id.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritsDoc}
|
||||||
|
* @return something like ".minecraft/versions/<version name>/<version name>-natives"
|
||||||
|
*/
|
||||||
override fun getNativeDirectory(id: String) = File(getVersionRoot(id), "$id-natives")
|
override fun getNativeDirectory(id: String) = File(getVersionRoot(id), "$id-natives")
|
||||||
override fun getVersionRoot(id: String) = File(baseDirectory, "versions/$id")
|
override fun getVersionRoot(id: String) = File(baseDirectory, "versions/$id")
|
||||||
open fun getVersionJson(id: String) = File(getVersionRoot(id), "$id.json")
|
open fun getVersionJson(id: String) = File(getVersionRoot(id), "$id.json")
|
||||||
@@ -83,7 +94,6 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected open fun refreshVersionsImpl() {
|
protected open fun refreshVersionsImpl() {
|
||||||
|
|
||||||
versions.clear()
|
versions.clear()
|
||||||
|
|
||||||
if (ClassicVersion.hasClassicVersion(baseDirectory)) {
|
if (ClassicVersion.hasClassicVersion(baseDirectory)) {
|
||||||
@@ -167,7 +177,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
return assetDir.resolve("objects/${obj.location}")
|
return assetDir.resolve("objects/${obj.location}")
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getIndexFile(version: String, assetId: String): File =
|
override fun getIndexFile(version: String, assetId: String): File =
|
||||||
getAssetDirectory(version, assetId).resolve("indexes/$assetId.json")
|
getAssetDirectory(version, assetId).resolve("indexes/$assetId.json")
|
||||||
|
|
||||||
override fun getLoggingObject(version: String, assetId: String, loggingInfo: LoggingInfo): File =
|
override fun getLoggingObject(version: String, assetId: String, loggingInfo: LoggingInfo): File =
|
||||||
@@ -176,9 +186,8 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
@Throws(IOException::class, JsonSyntaxException::class)
|
@Throws(IOException::class, JsonSyntaxException::class)
|
||||||
protected open fun reconstructAssets(version: String, assetId: String): File {
|
protected open fun reconstructAssets(version: String, assetId: String): File {
|
||||||
val assetsDir = getAssetDirectory(version, assetId)
|
val assetsDir = getAssetDirectory(version, assetId)
|
||||||
val assetVersion = assetId
|
val indexFile: File = getIndexFile(version, assetId)
|
||||||
val indexFile: File = getIndexFile(version, assetVersion)
|
val virtualRoot = assetsDir.resolve("virtual").resolve(assetId)
|
||||||
val virtualRoot = assetsDir.resolve("virtual").resolve(assetVersion)
|
|
||||||
|
|
||||||
if (!indexFile.isFile) {
|
if (!indexFile.isFile) {
|
||||||
return assetsDir
|
return assetsDir
|
||||||
|
|||||||
@@ -18,13 +18,15 @@
|
|||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supports operations on versioning.
|
* Supports operations on versioning.
|
||||||
*
|
*
|
||||||
* Note that game repository will not do any tasks that connect to Internet.
|
* Note that game repository will not do any tasks that connect to Internet, if do, see [org.jackhuang.hmcl.download.DependencyManager]
|
||||||
*/
|
*/
|
||||||
interface GameRepository : VersionProvider {
|
interface GameRepository : VersionProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the version of id exist?
|
* Does the version of id exist?
|
||||||
* @param id the id of version
|
* @param id the id of version
|
||||||
@@ -126,7 +128,8 @@ interface GameRepository : VersionProvider {
|
|||||||
* Will reconstruct assets or do some blocking tasks if necessary.
|
* Will reconstruct assets or do some blocking tasks if necessary.
|
||||||
* You'd better create a new thread to invoke this method.
|
* You'd better create a new thread to invoke this method.
|
||||||
*
|
*
|
||||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
* @throws java.io.IOException if I/O operation fails.
|
* @throws java.io.IOException if I/O operation fails.
|
||||||
* @return the actual asset directory
|
* @return the actual asset directory
|
||||||
*/
|
*/
|
||||||
@@ -134,24 +137,31 @@ interface GameRepository : VersionProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the asset directory according to the asset id.
|
* Get the asset directory according to the asset id.
|
||||||
|
*
|
||||||
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
|
* @return the asset directory
|
||||||
*/
|
*/
|
||||||
fun getAssetDirectory(version: String, assetId: String): File
|
fun getAssetDirectory(version: String, assetId: String): File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the file that given asset object refers to
|
* Get the file that given asset object refers to
|
||||||
*
|
*
|
||||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
* @param name the asset object name, [AssetIndex.objects.key]
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
|
* @param name the asset object name, you can find it in [AssetIndex.objects.keys]
|
||||||
* @throws java.io.IOException if I/O operation fails.
|
* @throws java.io.IOException if I/O operation fails.
|
||||||
* @return the file that given asset object refers to
|
* @return the file that given asset object refers to
|
||||||
*/
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
fun getAssetObject(version: String, assetId: String, name: String): File
|
fun getAssetObject(version: String, assetId: String, name: String): File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the file that given asset object refers to
|
* Get the file that given asset object refers to
|
||||||
*
|
*
|
||||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
* @param obj the asset object, [AssetIndex.objects]
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
|
* @param obj the asset object, you can find it in [AssetIndex.objects]
|
||||||
* @return the file that given asset object refers to
|
* @return the file that given asset object refers to
|
||||||
*/
|
*/
|
||||||
fun getAssetObject(version: String, assetId: String, obj: AssetObject): File
|
fun getAssetObject(version: String, assetId: String, obj: AssetObject): File
|
||||||
@@ -159,17 +169,28 @@ interface GameRepository : VersionProvider {
|
|||||||
/**
|
/**
|
||||||
* Get asset index that assetId represents
|
* Get asset index that assetId represents
|
||||||
*
|
*
|
||||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
* @return the asset index
|
* @return the asset index
|
||||||
*/
|
*/
|
||||||
fun getAssetIndex(version: String, assetId: String): AssetIndex
|
fun getAssetIndex(version: String, assetId: String): AssetIndex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the asset_index.json which includes asset objects information.
|
||||||
|
*
|
||||||
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.actualAssetIndex.id]
|
||||||
|
*/
|
||||||
|
fun getIndexFile(version: String, assetId: String): File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get logging object
|
* Get logging object
|
||||||
*
|
*
|
||||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
* @param version the id of specific version that is relevant to [assetId]
|
||||||
|
* @param assetId the asset id, you can find it in [AssetIndexInfo.id] [Version.assets]
|
||||||
* @param loggingInfo the logging info
|
* @param loggingInfo the logging info
|
||||||
* @return the file that loggingInfo refers to
|
* @return the file that loggingInfo refers to
|
||||||
*/
|
*/
|
||||||
fun getLoggingObject(version: String, assetId: String, loggingInfo: LoggingInfo): File
|
fun getLoggingObject(version: String, assetId: String, loggingInfo: LoggingInfo): File
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,10 +19,10 @@ package org.jackhuang.hmcl.game
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.util.closeQuietly
|
import org.jackhuang.hmcl.util.closeQuietly
|
||||||
import org.jackhuang.hmcl.util.readFullyAsByteArray
|
import org.jackhuang.hmcl.util.readFullyAsByteArray
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
private fun lessThan32(b: ByteArray, startIndex: Int): Int {
|
private fun lessThan32(b: ByteArray, startIndex: Int): Int {
|
||||||
for (i in startIndex until b.size)
|
for (i in startIndex until b.size)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import org.jackhuang.hmcl.util.Immutable
|
import org.jackhuang.hmcl.util.Immutable
|
||||||
import org.jackhuang.hmcl.util.Validation
|
import org.jackhuang.hmcl.util.Validation
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ package org.jackhuang.hmcl.game
|
|||||||
* @see Version.resolve
|
* @see Version.resolve
|
||||||
*/
|
*/
|
||||||
interface VersionProvider {
|
interface VersionProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the version of id exist?
|
* Does the version of id exist?
|
||||||
* @param id the id of version
|
* @param id the id of version
|
||||||
@@ -36,4 +37,5 @@ interface VersionProvider {
|
|||||||
* @return the version you want
|
* @return the version you want
|
||||||
*/
|
*/
|
||||||
fun getVersion(id: String): Version
|
fun getVersion(id: String): Version
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,14 +18,16 @@
|
|||||||
package org.jackhuang.hmcl.launch
|
package org.jackhuang.hmcl.launch
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthInfo
|
import org.jackhuang.hmcl.auth.AuthInfo
|
||||||
import org.jackhuang.hmcl.game.*
|
import org.jackhuang.hmcl.game.DownloadType
|
||||||
|
import org.jackhuang.hmcl.game.GameException
|
||||||
|
import org.jackhuang.hmcl.game.GameRepository
|
||||||
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
import org.jackhuang.hmcl.task.TaskResult
|
import org.jackhuang.hmcl.task.TaskResult
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.coroutines.experimental.EmptyCoroutineContext.plus
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param versionId The version to be launched.
|
* @param versionId The version to be launched.
|
||||||
@@ -220,7 +222,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launch(): JavaProcess {
|
override fun launch(): ManagedProcess {
|
||||||
|
|
||||||
// To guarantee that when failed to generate code, we will not call precalled command
|
// To guarantee that when failed to generate code, we will not call precalled command
|
||||||
val builder = ProcessBuilder(rawCommandLine)
|
val builder = ProcessBuilder(rawCommandLine)
|
||||||
@@ -237,7 +239,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
|
|
||||||
builder.directory(repository.getRunDirectory(version.id))
|
builder.directory(repository.getRunDirectory(version.id))
|
||||||
.environment().put("APPDATA", options.gameDir.absoluteFile.parent)
|
.environment().put("APPDATA", options.gameDir.absoluteFile.parent)
|
||||||
val p = JavaProcess(builder.start(), rawCommandLine)
|
val p = ManagedProcess(builder.start(), rawCommandLine)
|
||||||
if (listener == null)
|
if (listener == null)
|
||||||
startMonitors(p)
|
startMonitors(p)
|
||||||
else
|
else
|
||||||
@@ -245,8 +247,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchAsync(): TaskResult<JavaProcess> {
|
fun launchAsync(): TaskResult<ManagedProcess> {
|
||||||
return object : TaskResult<JavaProcess>() {
|
return object : TaskResult<ManagedProcess>() {
|
||||||
override val id = LAUNCH_ASYNC_ID
|
override val id = LAUNCH_ASYNC_ID
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
result = launch()
|
result = launch()
|
||||||
@@ -279,20 +281,20 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
return scriptFile
|
return scriptFile
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startMonitors(javaProcess: JavaProcess) {
|
private fun startMonitors(managedProcess: ManagedProcess) {
|
||||||
javaProcess.relatedThreads += thread(name = "stdout-pump", isDaemon = true, block = StreamPump(javaProcess.process.inputStream)::run)
|
managedProcess.relatedThreads += thread(name = "stdout-pump", isDaemon = true, block = StreamPump(managedProcess.process.inputStream)::run)
|
||||||
javaProcess.relatedThreads += thread(name = "stderr-pump", isDaemon = true, block = StreamPump(javaProcess.process.errorStream)::run)
|
managedProcess.relatedThreads += thread(name = "stderr-pump", isDaemon = true, block = StreamPump(managedProcess.process.errorStream)::run)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startMonitors(javaProcess: JavaProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
||||||
processListener.setProcess(javaProcess)
|
processListener.setProcess(managedProcess)
|
||||||
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); javaProcess.lines += line }.apply { start() }
|
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() }
|
||||||
javaProcess.relatedThreads += logHandler
|
managedProcess.relatedThreads += logHandler
|
||||||
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.inputStream, { logHandler.newLine(it) } )::run)
|
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.inputStream, { logHandler.newLine(it) } )::run)
|
||||||
javaProcess.relatedThreads += stdout
|
managedProcess.relatedThreads += stdout
|
||||||
val stderr = thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.errorStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.ERROR); javaProcess.lines += it })::run)
|
val stderr = thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.errorStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.ERROR); managedProcess.lines += it })::run)
|
||||||
javaProcess.relatedThreads += stderr
|
managedProcess.relatedThreads += stderr
|
||||||
javaProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(javaProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run)
|
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -21,16 +21,15 @@ import org.jackhuang.hmcl.event.EVENT_BUS
|
|||||||
import org.jackhuang.hmcl.event.JVMLaunchFailedEvent
|
import org.jackhuang.hmcl.event.JVMLaunchFailedEvent
|
||||||
import org.jackhuang.hmcl.event.JavaProcessExitedAbnormallyEvent
|
import org.jackhuang.hmcl.event.JavaProcessExitedAbnormallyEvent
|
||||||
import org.jackhuang.hmcl.event.JavaProcessStoppedEvent
|
import org.jackhuang.hmcl.event.JavaProcessStoppedEvent
|
||||||
import org.jackhuang.hmcl.util.JavaProcess
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
import org.jackhuang.hmcl.util.containsOne
|
import org.jackhuang.hmcl.util.containsOne
|
||||||
import org.jackhuang.hmcl.util.guessLogLineError
|
import org.jackhuang.hmcl.util.guessLogLineError
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param process the process to wait for
|
* @param process the process to wait for
|
||||||
* @param watcher the callback that will be called after process stops.
|
* @param watcher the callback that will be called after process stops.
|
||||||
*/
|
*/
|
||||||
internal class ExitWaiter(val process: JavaProcess, val joins: Collection<Thread>, val watcher: (Int, ProcessListener.ExitType) -> Unit) : Runnable {
|
internal class ExitWaiter(val process: ManagedProcess, val joins: Collection<Thread>, val watcher: (Int, ProcessListener.ExitType) -> Unit) : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
try {
|
try {
|
||||||
process.process.waitFor()
|
process.process.waitFor()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.jackhuang.hmcl.auth.AuthInfo
|
|||||||
import org.jackhuang.hmcl.game.GameRepository
|
import org.jackhuang.hmcl.game.GameRepository
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
import org.jackhuang.hmcl.util.JavaProcess
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class Launcher(
|
abstract class Launcher(
|
||||||
@@ -34,7 +34,7 @@ abstract class Launcher(
|
|||||||
|
|
||||||
val version: Version = repository.getVersion(versionId).resolve(repository)
|
val version: Version = repository.getVersion(versionId).resolve(repository)
|
||||||
abstract val rawCommandLine: List<String>
|
abstract val rawCommandLine: List<String>
|
||||||
abstract fun launch(): JavaProcess
|
abstract fun launch(): ManagedProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param file The file path without extension
|
* @param file The file path without extension
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is to parse log4j classic XML layout logging, since only vanilla Minecraft will enable this layout.
|
* This class is to parse log4j classic XML layout logging, since only vanilla Minecraft will enable this layout.
|
||||||
|
* Also supports plain logs.
|
||||||
*/
|
*/
|
||||||
internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread() {
|
internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread() {
|
||||||
val reader = XMLReaderFactory.createXMLReader().apply {
|
val reader = XMLReaderFactory.createXMLReader().apply {
|
||||||
@@ -53,6 +54,9 @@ internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called to stop [Log4jHandler] manually.
|
||||||
|
*/
|
||||||
fun onStopped() {
|
fun onStopped() {
|
||||||
if (interrupted.get())
|
if (interrupted.get())
|
||||||
return
|
return
|
||||||
@@ -66,7 +70,7 @@ internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called in [ProcessListener.onErrorLog] and [ProcessListener.onLog] manually.
|
* New XML line.
|
||||||
*/
|
*/
|
||||||
fun newLine(content: String) =
|
fun newLine(content: String) =
|
||||||
Scheduler.COMPUTATION.schedule {
|
Scheduler.COMPUTATION.schedule {
|
||||||
@@ -102,6 +106,7 @@ internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread
|
|||||||
l = Log4jLevel.ERROR
|
l = Log4jLevel.ERROR
|
||||||
}
|
}
|
||||||
"log4j_Message" -> readingMessage = true
|
"log4j_Message" -> readingMessage = true
|
||||||
|
"log4j_Throwable" -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.launch
|
package org.jackhuang.hmcl.launch
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.JavaProcess
|
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel
|
import org.jackhuang.hmcl.util.Log4jLevel
|
||||||
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
|
|
||||||
interface ProcessListener {
|
interface ProcessListener {
|
||||||
/**
|
/**
|
||||||
* When a game launched, this method will be called to get the new process.
|
* When a game launched, this method will be called to get the new process.
|
||||||
* You should not override this method when your ProcessListener is shared with all processes.
|
* You should not override this method when your ProcessListener is shared with all processes.
|
||||||
*/
|
*/
|
||||||
fun setProcess(process: JavaProcess) {}
|
fun setProcess(process: ManagedProcess) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when receiving a log from stdout/stderr.
|
* Called when receiving a log from stdout/stderr.
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ import java.io.IOException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pump the given input stream.
|
||||||
|
* @param inputStream the input stream to pump
|
||||||
|
* @param callback receives each line
|
||||||
|
*
|
||||||
|
*/
|
||||||
internal class StreamPump @JvmOverloads constructor(
|
internal class StreamPump @JvmOverloads constructor(
|
||||||
private val inputStream: InputStream,
|
private val inputStream: InputStream,
|
||||||
private val callback: (String) -> Unit = {}
|
private val callback: (String) -> Unit = {}
|
||||||
|
|||||||
@@ -99,14 +99,27 @@ class CurseForgeModpackManifestFile (
|
|||||||
val url: URL get() = "https://minecraft.curseforge.com/projects/$projectID/files/$fileID/download".toURL()
|
val url: URL get() = "https://minecraft.curseforge.com/projects/$projectID/files/$fileID/download".toURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param f the CurseForge modpack file.
|
||||||
|
* @return the manifest.
|
||||||
|
*/
|
||||||
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
||||||
ZipFile(f).use { zipFile ->
|
ZipFile(f).use { zipFile ->
|
||||||
val entry = zipFile.getEntry("manifest.json") ?: throw IOException("Manifest.json not found. Not a valid CurseForge modpack.")
|
val entry = zipFile.getEntry("manifest.json") ?: throw IOException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||||
val json = zipFile.getInputStream(entry).readFullyAsString()
|
val json = zipFile.getInputStream(entry).readFullyAsString()
|
||||||
return GSON.fromJson<CurseForgeModpackManifest>(json) ?: throw JsonParseException("Manifest.json not found. Not a valid CurseForge modpack.")
|
return GSON.fromJson<CurseForgeModpackManifest>(json) ?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install a downloaded CurseForge modpack.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager.
|
||||||
|
* @param zipFile the CurseForge modpack file.
|
||||||
|
* @param manifest The manifest content of given CurseForge modpack.
|
||||||
|
* @param name the new version name
|
||||||
|
* @see readCurseForgeModpackManifest
|
||||||
|
*/
|
||||||
class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDependencyManager, private val zipFile: File, private val manifest: CurseForgeModpackManifest, private val name: String): Task() {
|
class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDependencyManager, private val zipFile: File, private val manifest: CurseForgeModpackManifest, private val name: String): Task() {
|
||||||
val repository = dependencyManager.repository
|
val repository = dependencyManager.repository
|
||||||
init {
|
init {
|
||||||
@@ -136,7 +149,8 @@ class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDepende
|
|||||||
f.fileName = f.url.detectFileName(dependencyManager.proxy)
|
f.fileName = f.url.detectFileName(dependencyManager.proxy)
|
||||||
dependencies += FileDownloadTask(f.url, run.resolve("mods").resolve(f.fileName), proxy = dependencyManager.proxy)
|
dependencies += FileDownloadTask(f.url, run.resolve("mods").resolve(f.fileName), proxy = dependencyManager.proxy)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
// ignore it and retry next time.
|
// Because in China, the CurseForge is too difficult to visit.
|
||||||
|
// So if failed, ignore it and retry next time.
|
||||||
}
|
}
|
||||||
++finished
|
++finished
|
||||||
updateProgress(1.0 * finished / manifest.files.size)
|
updateProgress(1.0 * finished / manifest.files.size)
|
||||||
@@ -146,6 +160,12 @@ class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDepende
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete the CurseForge version.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager.
|
||||||
|
* @param version the existent and physical version.
|
||||||
|
*/
|
||||||
class CurseForgeModpackCompletionTask(dependencyManager: DependencyManager, version: String): Task() {
|
class CurseForgeModpackCompletionTask(dependencyManager: DependencyManager, version: String): Task() {
|
||||||
val repository = dependencyManager.repository
|
val repository = dependencyManager.repository
|
||||||
val run = repository.getRunDirectory(version)
|
val run = repository.getRunDirectory(version)
|
||||||
@@ -161,6 +181,8 @@ class CurseForgeModpackCompletionTask(dependencyManager: DependencyManager, vers
|
|||||||
else {
|
else {
|
||||||
manifest = GSON.fromJson<CurseForgeModpackManifest>(repository.getVersionRoot(version).resolve("manifest.json").readText())!!
|
manifest = GSON.fromJson<CurseForgeModpackManifest>(repository.getVersionRoot(version).resolve("manifest.json").readText())!!
|
||||||
|
|
||||||
|
// Because in China, the CurseForge is too difficult to visit.
|
||||||
|
// So caching the file name is necessary.
|
||||||
for (f in manifest!!.files) {
|
for (f in manifest!!.files) {
|
||||||
if (f.fileName.isBlank())
|
if (f.fileName.isBlank())
|
||||||
dependents += task { f.fileName = f.url.detectFileName(proxy) }
|
dependents += task { f.fileName = f.url.detectFileName(proxy) }
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import org.jackhuang.hmcl.util.typeOf
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class ForgeModMetadata(
|
class ForgeModMetadata @JvmOverloads internal constructor(
|
||||||
@SerializedName("modid")
|
@SerializedName("modid")
|
||||||
val modId: String = "",
|
val modId: String = "",
|
||||||
val name: String = "",
|
val name: String = "",
|
||||||
@@ -42,6 +42,9 @@ class ForgeModMetadata(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* Read Forge mod ModInfo.
|
||||||
|
*/
|
||||||
fun fromFile(modFile: File): ModInfo {
|
fun fromFile(modFile: File): ModInfo {
|
||||||
ZipFile(modFile).use {
|
ZipFile(modFile).use {
|
||||||
val entry = it.getEntry("mcmod.info") ?: throw JsonParseException("File $modFile is not a Forge mod.")
|
val entry = it.getEntry("mcmod.info") ?: throw JsonParseException("File $modFile is not a Forge mod.")
|
||||||
|
|||||||
@@ -18,11 +18,13 @@
|
|||||||
package org.jackhuang.hmcl.mod
|
package org.jackhuang.hmcl.mod
|
||||||
|
|
||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.GSON
|
||||||
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
|
import org.jackhuang.hmcl.util.readFullyAsString
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class LiteModMetadata (
|
class LiteModMetadata @JvmOverloads internal constructor(
|
||||||
val name: String = "",
|
val name: String = "",
|
||||||
val version: String = "",
|
val version: String = "",
|
||||||
val mcversion: String = "",
|
val mcversion: String = "",
|
||||||
@@ -37,6 +39,9 @@ class LiteModMetadata (
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* Read LiteLoader mod ModInfo.
|
||||||
|
*/
|
||||||
fun fromFile(modFile: File): ModInfo {
|
fun fromFile(modFile: File): ModInfo {
|
||||||
ZipFile(modFile).use {
|
ZipFile(modFile).use {
|
||||||
val entry = it.getEntry("litemod.json")
|
val entry = it.getEntry("litemod.json")
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.mod
|
package org.jackhuang.hmcl.mod
|
||||||
|
|
||||||
import com.google.gson.JsonParseException
|
import org.jackhuang.hmcl.util.ImmediateBooleanProperty
|
||||||
import org.jackhuang.hmcl.util.property.ImmediateBooleanProperty
|
import org.jackhuang.hmcl.util.getValue
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.setValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class ModInfo (
|
class ModInfo (
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.mod
|
package org.jackhuang.hmcl.mod
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Modpack(val file: File) {
|
class Modpack(val file: File) {
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ package org.jackhuang.hmcl.task
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that combines two tasks and make sure [pred] runs before [succ].
|
||||||
|
*
|
||||||
|
* @param pred the task that runs before [succ]
|
||||||
|
* @param succ a callback that returns the task runs after [pred], [succ] will be executed asynchronously. You can do something that relies on the result of [pred].
|
||||||
|
* @param reliant true if this task chain will be broken when task [pred] fails.
|
||||||
|
*/
|
||||||
internal class CoupleTask<P: Task>(pred: P, private val succ: (AutoTypingMap<String>) -> Task?, override val reliant: Boolean) : Task() {
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -19,28 +19,39 @@ package org.jackhuang.hmcl.task
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.event.EventManager
|
import org.jackhuang.hmcl.event.EventManager
|
||||||
import org.jackhuang.hmcl.event.FailedEvent
|
import org.jackhuang.hmcl.event.FailedEvent
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.closeQuietly
|
|
||||||
import org.jackhuang.hmcl.util.DigestUtils
|
|
||||||
import org.jackhuang.hmcl.util.makeDirectory
|
|
||||||
import org.jackhuang.hmcl.util.createConnection
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
|
import java.math.BigInteger
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.math.BigInteger
|
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that can download a file online.
|
||||||
|
*
|
||||||
|
* @param url the URL of remote file.
|
||||||
|
* @param file the location that download to.
|
||||||
|
* @param hash the SHA-1 hash code of remote file, null if the hash is unknown or it is no need to check the hash code.
|
||||||
|
* @param retry the times for retrying if downloading fails.
|
||||||
|
* @param proxy the proxy.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
class FileDownloadTask @JvmOverloads constructor(val url: URL, val file: File, val hash: String? = null, val retry: Int = 5, val proxy: Proxy = Proxy.NO_PROXY): Task() {
|
class FileDownloadTask @JvmOverloads constructor(val url: URL, val file: File, val hash: String? = null, val retry: Int = 5, val proxy: Proxy = Proxy.NO_PROXY): Task() {
|
||||||
override val scheduler: Scheduler = Scheduler.IO
|
override val scheduler: Scheduler = Scheduler.IO
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once downloading fails, this event will be fired to gain the substitute URL.
|
||||||
|
*/
|
||||||
var onFailed = EventManager<FailedEvent<URL>>()
|
var onFailed = EventManager<FailedEvent<URL>>()
|
||||||
|
|
||||||
private var rFile: RandomAccessFile? = null
|
private var rFile: RandomAccessFile? = null
|
||||||
private var stream: InputStream? = null
|
private var stream: InputStream? = null
|
||||||
|
|
||||||
fun closeFiles() {
|
private fun closeFiles() {
|
||||||
rFile?.closeQuietly()
|
rFile?.closeQuietly()
|
||||||
rFile = null
|
rFile = null
|
||||||
stream?.closeQuietly()
|
stream?.closeQuietly()
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ import java.net.Proxy
|
|||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that can read the content of a remote text file.
|
||||||
|
*
|
||||||
|
* @param url the URL of remote text file.
|
||||||
|
* @param encoding the encoding/charset of the remote text file.
|
||||||
|
* @param retry the times for retrying if downloading fails.
|
||||||
|
* @param proxy the proxy.
|
||||||
|
* @param id the result variable id, see [Task.variables]
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
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>() {
|
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
|
||||||
|
|
||||||
@@ -65,6 +76,9 @@ class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* The default task result ID.
|
||||||
|
*/
|
||||||
const val ID = "http_get"
|
const val ID = "http_get"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tasks that provides a way to execute tasks parallelly.
|
||||||
|
*
|
||||||
|
* @param tasks the tasks that can be executed parallelly.
|
||||||
|
*/
|
||||||
class ParallelTask(vararg tasks: Task): Task() {
|
class ParallelTask(vararg tasks: Task): Task() {
|
||||||
override val hidden: Boolean = true
|
override val hidden: Boolean = true
|
||||||
override val dependents: Collection<Task> = listOf(*tasks)
|
override val dependents: Collection<Task> = listOf(*tasks)
|
||||||
|
|||||||
@@ -17,13 +17,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
import javafx.application.Platform
|
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import javax.swing.SwingUtilities
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how a task is executed.
|
||||||
|
*
|
||||||
|
* @see [Task.scheduler]
|
||||||
|
*/
|
||||||
interface Scheduler {
|
interface Scheduler {
|
||||||
|
/**
|
||||||
|
* Schedules the given task.
|
||||||
|
* @return the future, null if future is not supported.
|
||||||
|
*/
|
||||||
fun schedule(block: () -> Unit): Future<*>? = schedule(Callable { block() })
|
fun schedule(block: () -> Unit): Future<*>? = schedule(Callable { block() })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules the given task.
|
||||||
|
* @return the future, null if future is not supported.
|
||||||
|
*/
|
||||||
fun schedule(block: Callable<Unit>): Future<*>?
|
fun schedule(block: Callable<Unit>): Future<*>?
|
||||||
|
|
||||||
private class SchedulerImpl(val executor: (Runnable) -> Unit) : Scheduler {
|
private class SchedulerImpl(val executor: (Runnable) -> Unit) : Scheduler {
|
||||||
@@ -58,7 +70,7 @@ interface Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SchedulerExecutorService(val executorService: ExecutorService) : Scheduler {
|
private class SchedulerExecutorService(val executorService: ExecutorService) : Scheduler {
|
||||||
override fun schedule(block: Callable<Unit>) = executorService.submit(block)
|
override fun schedule(block: Callable<Unit>): Future<*> = executorService.submit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object Schedulers {
|
companion object Schedulers {
|
||||||
@@ -90,13 +102,44 @@ interface Scheduler {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val JAVAFX: Scheduler = SchedulerImpl(Platform::runLater)
|
|
||||||
val SWING: Scheduler = SchedulerImpl(SwingUtilities::invokeLater)
|
/**
|
||||||
|
* A scheduler for JavaFX UI operations.
|
||||||
|
*/
|
||||||
|
val JAVAFX: Scheduler = SchedulerImpl(javafx.application.Platform::runLater)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduler for Swing UI operations.
|
||||||
|
*/
|
||||||
|
val SWING: Scheduler = SchedulerImpl(javax.swing.SwingUtilities::invokeLater)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduler that always create new threads to execute tasks.
|
||||||
|
* For tasks that do not do heavy operations.
|
||||||
|
*/
|
||||||
val NEW_THREAD: Scheduler = SchedulerExecutorService(CACHED_EXECUTOR)
|
val NEW_THREAD: Scheduler = SchedulerExecutorService(CACHED_EXECUTOR)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduler that exclusively executes tasks that do I/O operations.
|
||||||
|
* The number tasks that do I/O operations in the meantime cannot be larger then 6.
|
||||||
|
*/
|
||||||
val IO: Scheduler = SchedulerExecutorService(IO_EXECUTOR)
|
val IO: Scheduler = SchedulerExecutorService(IO_EXECUTOR)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduler that exclusively executes tasks that do computations.
|
||||||
|
* The best way to do computations is an event queue.
|
||||||
|
*/
|
||||||
val COMPUTATION: Scheduler = SchedulerExecutorService(SINGLE_EXECUTOR)
|
val COMPUTATION: Scheduler = SchedulerExecutorService(SINGLE_EXECUTOR)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default scheduler for tasks to be executed.
|
||||||
|
* @see [Task.scheduler]
|
||||||
|
*/
|
||||||
val DEFAULT = NEW_THREAD
|
val DEFAULT = NEW_THREAD
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down all executor services to guarantee that the application can stop implicitly.
|
||||||
|
*/
|
||||||
fun shutdown() {
|
fun shutdown() {
|
||||||
CACHED_EXECUTOR.shutdown()
|
CACHED_EXECUTOR.shutdown()
|
||||||
IO_EXECUTOR.shutdown()
|
IO_EXECUTOR.shutdown()
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
class SilentException : Exception {
|
/**
|
||||||
constructor() : super() {}
|
* If a task throws [SilentException], the task will be marked as failure but do not log the stacktrace.
|
||||||
constructor(message: String) : super(message) {}
|
*/
|
||||||
constructor(message: String, cause: Throwable) : super(message, cause) {}
|
class SilentException : Exception()
|
||||||
}
|
|
||||||
@@ -22,12 +22,15 @@ import javafx.beans.property.ReadOnlyDoubleWrapper
|
|||||||
import javafx.beans.property.ReadOnlyStringProperty
|
import javafx.beans.property.ReadOnlyStringProperty
|
||||||
import javafx.beans.property.ReadOnlyStringWrapper
|
import javafx.beans.property.ReadOnlyStringWrapper
|
||||||
import org.jackhuang.hmcl.event.EventManager
|
import org.jackhuang.hmcl.event.EventManager
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
|
import org.jackhuang.hmcl.util.updateAsync
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposable task.
|
* Disposable task.
|
||||||
|
*
|
||||||
|
* @see [TaskExecutor]
|
||||||
*/
|
*/
|
||||||
abstract class Task {
|
abstract class Task {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ package org.jackhuang.hmcl.task
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.Callable
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.Future
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class TaskExecutor() {
|
class TaskExecutor() {
|
||||||
var taskListener: TaskListener? = null
|
var taskListener: TaskListener? = null
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that has a result.
|
||||||
|
*/
|
||||||
abstract class TaskResult<V> : Task() {
|
abstract class TaskResult<V> : Task() {
|
||||||
open var result: V? = null
|
open var result: V? = null
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark if the model is immutable.
|
||||||
|
*/
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
annotation class Immutable
|
annotation class Immutable
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map that support auto casting.
|
||||||
|
*/
|
||||||
class AutoTypingMap<K>(private val impl: MutableMap<K, Any>) {
|
class AutoTypingMap<K>(private val impl: MutableMap<K, Any>) {
|
||||||
|
|
||||||
fun clear() = impl.clear()
|
fun clear() = impl.clear()
|
||||||
|
|||||||
@@ -18,10 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.StandardCopyOption
|
|
||||||
|
|
||||||
fun File.makeDirectory(): Boolean = isDirectory || mkdirs()
|
fun File.makeDirectory(): Boolean = isDirectory || mkdirs()
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,12 @@ package org.jackhuang.hmcl.util
|
|||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
|
||||||
import java.lang.reflect.Type
|
|
||||||
import com.google.gson.stream.JsonToken
|
import com.google.gson.stream.JsonToken
|
||||||
import java.io.IOException
|
import com.google.gson.stream.JsonWriter
|
||||||
import com.google.gson.TypeAdapter
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.TypeAdapterFactory
|
|
||||||
import org.jackhuang.hmcl.game.Library
|
import org.jackhuang.hmcl.game.Library
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.lang.reflect.Type
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ fun InputStream.copyToAndClose(dest: OutputStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cmd the command line
|
||||||
|
* @return the final command line
|
||||||
|
*/
|
||||||
fun makeCommand(cmd: List<String>): String {
|
fun makeCommand(cmd: List<String>): String {
|
||||||
val cmdbuf = StringBuilder(120)
|
val cmdbuf = StringBuilder(120)
|
||||||
for (i in cmd.indices) {
|
for (i in cmd.indices) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util.property
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
import javafx.beans.property.*
|
import javafx.beans.property.*
|
||||||
import javafx.beans.value.ChangeListener
|
import javafx.beans.value.ChangeListener
|
||||||
@@ -24,12 +24,26 @@ import java.io.Serializable
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Java installation.
|
||||||
|
*/
|
||||||
data class JavaVersion internal constructor(
|
data class JavaVersion internal constructor(
|
||||||
@SerializedName("location")
|
@SerializedName("location")
|
||||||
val binary: File,
|
val binary: File,
|
||||||
val longVersion: String,
|
val longVersion: String,
|
||||||
val platform: Platform) : Serializable
|
val platform: Platform) : Serializable
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The major version of Java installation.
|
||||||
|
*
|
||||||
|
* @see JAVA_X
|
||||||
|
* @see JAVA_9
|
||||||
|
* @see JAVA_8
|
||||||
|
* @see JAVA_7
|
||||||
|
* @see JAVA_6
|
||||||
|
* @see JAVA_5
|
||||||
|
* @see UNKNOWN
|
||||||
|
*/
|
||||||
val version = parseVersion(longVersion)
|
val version = parseVersion(longVersion)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -110,10 +124,12 @@ data class JavaVersion internal constructor(
|
|||||||
return path.resolve("java")
|
return path.resolve("java")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentJava: JavaVersion = JavaVersion(
|
private val currentJava: JavaVersion by lazy {
|
||||||
|
JavaVersion(
|
||||||
binary = getJavaFile(File(System.getProperty("java.home"))),
|
binary = getJavaFile(File(System.getProperty("java.home"))),
|
||||||
longVersion = System.getProperty("java.version"),
|
longVersion = System.getProperty("java.version"),
|
||||||
platform = Platform.PLATFORM)
|
platform = Platform.PLATFORM)
|
||||||
|
}
|
||||||
fun fromCurrentEnvironment() = currentJava
|
fun fromCurrentEnvironment() = currentJava
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -18,12 +18,9 @@
|
|||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
import javafx.beans.property.Property
|
import javafx.beans.property.Property
|
||||||
import javafx.beans.value.ObservableValue
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
import sun.text.normalizer.UTF16.append
|
|
||||||
import java.lang.reflect.Array.getLength
|
|
||||||
|
|
||||||
inline fun ignoreException(func: () -> Unit) {
|
inline fun ignoreException(func: () -> Unit) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -18,13 +18,13 @@
|
|||||||
@file:JvmName("HMCLog")
|
@file:JvmName("HMCLog")
|
||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.logging.*
|
import java.util.logging.*
|
||||||
import java.util.logging.Formatter
|
import java.util.logging.Formatter
|
||||||
import javafx.scene.paint.Color
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
val LOG = Logger.getLogger("HMCL").apply {
|
val LOG = Logger.getLogger("HMCL").apply {
|
||||||
|
|||||||
@@ -20,25 +20,58 @@ package org.jackhuang.hmcl.util
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
class JavaProcess(
|
/**
|
||||||
|
* The managed process.
|
||||||
|
*
|
||||||
|
* @param process the raw system process that this instance manages.
|
||||||
|
* @param commands the command line of [process].
|
||||||
|
* @see [org.jackhuang.hmcl.launch.ExitWaiter]
|
||||||
|
* @see [org.jackhuang.hmcl.launch.StreamPump]
|
||||||
|
*/
|
||||||
|
class ManagedProcess(
|
||||||
val process: Process,
|
val process: Process,
|
||||||
val commands: List<String>
|
val commands: List<String>
|
||||||
) {
|
) {
|
||||||
|
/**
|
||||||
|
* To save some information you need.
|
||||||
|
*/
|
||||||
val properties = mutableMapOf<String, Any>()
|
val properties = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard output/error lines.
|
||||||
|
*/
|
||||||
val lines: Queue<String> = ConcurrentLinkedQueue<String>()
|
val lines: Queue<String> = ConcurrentLinkedQueue<String>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The related threads.
|
||||||
|
*
|
||||||
|
* If a thread is monitoring this raw process,
|
||||||
|
* you are required to add the instance to [relatedThreads].
|
||||||
|
*/
|
||||||
val relatedThreads = mutableListOf<Thread>()
|
val relatedThreads = mutableListOf<Thread>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the managed process is running.
|
||||||
|
*/
|
||||||
val isRunning: Boolean = try {
|
val isRunning: Boolean = try {
|
||||||
process.exitValue()
|
process.exitValue()
|
||||||
true
|
true
|
||||||
} catch (ex: IllegalThreadStateException) {
|
} catch (ex: IllegalThreadStateException) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exit code of raw process.
|
||||||
|
*/
|
||||||
val exitCode: Int get() = process.exitValue()
|
val exitCode: Int get() = process.exitValue()
|
||||||
|
|
||||||
override fun toString() = "JavaProcess[commands=$commands, isRunning=$isRunning]"
|
/**
|
||||||
|
* Destroys the raw process and other related threads that are monitoring this raw process.
|
||||||
|
*/
|
||||||
fun stop() {
|
fun stop() {
|
||||||
process.destroy()
|
process.destroy()
|
||||||
relatedThreads.forEach(Thread::interrupt)
|
relatedThreads.forEach(Thread::interrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "ManagedProcess[commands=$commands, isRunning=$isRunning]"
|
||||||
}
|
}
|
||||||
@@ -19,20 +19,19 @@ package org.jackhuang.hmcl.util
|
|||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.GeneralSecurityException
|
import java.security.GeneralSecurityException
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.*
|
||||||
|
import java.util.logging.Level
|
||||||
import javax.net.ssl.HostnameVerifier
|
import javax.net.ssl.HostnameVerifier
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.X509TrustManager
|
import javax.net.ssl.X509TrustManager
|
||||||
import java.io.OutputStream
|
|
||||||
import java.util.*
|
|
||||||
import java.util.logging.Level
|
|
||||||
import kotlin.text.Charsets
|
|
||||||
|
|
||||||
private val XTM = object : X509TrustManager {
|
private val XTM = object : X509TrustManager {
|
||||||
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
|
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user