Javadoc
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl
|
||||
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import java.util.EventObject
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* This event gets fired when the selected profile changed.
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl
|
||||
import javafx.application.Application
|
||||
import javafx.application.Platform
|
||||
import javafx.stage.Stage
|
||||
import javafx.stage.StageStyle
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
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.OS
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.logging.Level
|
||||
|
||||
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.Settings
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
import java.io.File
|
||||
|
||||
@@ -29,15 +29,15 @@ import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.task.*
|
||||
import org.jackhuang.hmcl.ui.*
|
||||
import org.jackhuang.hmcl.util.JavaProcess
|
||||
import org.jackhuang.hmcl.util.Log4jLevel
|
||||
import org.jackhuang.hmcl.util.ManagedProcess
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
|
||||
object LauncherHelper {
|
||||
private val launchingStepsPane = LaunchingStepsPane()
|
||||
val PROCESS = ConcurrentLinkedQueue<JavaProcess>()
|
||||
val PROCESS = ConcurrentLinkedQueue<ManagedProcess>()
|
||||
|
||||
fun launch() {
|
||||
val profile = Settings.selectedProfile
|
||||
@@ -119,11 +119,11 @@ object LauncherHelper {
|
||||
authInfo.username to "<player>"
|
||||
)
|
||||
private val launcherVisibility = setting.launcherVisibility
|
||||
private lateinit var process: JavaProcess
|
||||
private lateinit var process: ManagedProcess
|
||||
private var lwjgl = false
|
||||
private var logWindow: LogWindow? = null
|
||||
private val logs = LinkedList<Pair<String, Log4jLevel>>()
|
||||
override fun setProcess(process: JavaProcess) {
|
||||
override fun setProcess(process: ManagedProcess) {
|
||||
this.process = process
|
||||
|
||||
if (setting.showLogs) {
|
||||
|
||||
@@ -20,8 +20,7 @@ package org.jackhuang.hmcl.setting
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.util.JavaVersion
|
||||
import java.io.File
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
|
||||
class Config {
|
||||
@SerializedName("last")
|
||||
|
||||
@@ -22,9 +22,10 @@ import javafx.beans.InvalidationListener
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||
import org.jackhuang.hmcl.mod.ModManager
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
||||
import org.jackhuang.hmcl.util.property.ImmediateStringProperty
|
||||
import org.jackhuang.hmcl.util.ImmediateObjectProperty
|
||||
import org.jackhuang.hmcl.util.ImmediateStringProperty
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
import java.io.File
|
||||
import java.lang.reflect.Type
|
||||
|
||||
|
||||
@@ -20,26 +20,23 @@ package org.jackhuang.hmcl.setting
|
||||
import com.google.gson.GsonBuilder
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.scene.text.Font
|
||||
import java.io.IOException
|
||||
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.DownloadProvider
|
||||
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.util.property.ImmediateObjectProperty
|
||||
import org.jackhuang.hmcl.util.property.ImmediateStringProperty
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.Authenticator
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.PasswordAuthentication
|
||||
import java.net.Proxy
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
object Settings {
|
||||
val GSON = GsonBuilder()
|
||||
|
||||
@@ -22,7 +22,6 @@ import javafx.beans.InvalidationListener
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.game.LaunchOptions
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.property.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Type
|
||||
|
||||
@@ -19,15 +19,14 @@ package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.*
|
||||
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.StringProperty
|
||||
import javafx.beans.value.ChangeListener
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||
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.taskResult
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class AccountsPage() : StackPane(), DecoratorPage {
|
||||
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 progressBar: JFXProgressBar
|
||||
|
||||
val listener = ChangeListener<Account?> { _, _, newValue ->
|
||||
masonryPane.children.forEach {
|
||||
if (it is AccountItem) {
|
||||
it.chkSelected.isSelected = newValue?.username == it.lblUser.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/account.fxml")
|
||||
children.remove(dialog)
|
||||
@@ -75,8 +67,8 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
}
|
||||
txtPassword.validate()
|
||||
|
||||
cboType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||
val visible = newValue != 0
|
||||
cboType.selectionModel.selectedIndexProperty().onChange {
|
||||
val visible = it != 0
|
||||
txtPassword.isVisible = visible
|
||||
}
|
||||
cboType.selectionModel.select(0)
|
||||
@@ -84,7 +76,13 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
txtPassword.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()
|
||||
|
||||
@@ -92,10 +90,6 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
addNewAccount()
|
||||
}
|
||||
|
||||
override fun onClose() {
|
||||
Settings.selectedAccountProperty.removeListener(listener)
|
||||
}
|
||||
|
||||
fun loadAccounts() {
|
||||
val children = mutableListOf<Node>()
|
||||
var i = 0
|
||||
@@ -103,9 +97,9 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
for ((_, account) in Settings.getAccounts()) {
|
||||
children += buildNode(++i, account, group)
|
||||
}
|
||||
group.selectedToggleProperty().addListener { _, _, newValue ->
|
||||
if (newValue != null)
|
||||
Settings.selectedAccount = newValue.properties["account"] as Account
|
||||
group.selectedToggleProperty().onChange {
|
||||
if (it != null)
|
||||
Settings.selectedAccount = it.properties["account"] as Account
|
||||
}
|
||||
masonryPane.resetChildren(children)
|
||||
Platform.runLater {
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.jfoenix.controls.JFXDrawer
|
||||
import com.jfoenix.controls.JFXHamburger
|
||||
import com.jfoenix.effects.JFXDepthManager
|
||||
import com.jfoenix.svg.SVGGlyph
|
||||
import javafx.animation.*
|
||||
import javafx.animation.Timeline
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.property.BooleanProperty
|
||||
import javafx.beans.property.ObjectProperty
|
||||
@@ -36,7 +36,6 @@ import javafx.geometry.Insets
|
||||
import javafx.scene.Cursor
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.control.Tooltip
|
||||
import javafx.scene.input.MouseEvent
|
||||
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.TransitionHandler
|
||||
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.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
|
||||
@@ -18,13 +18,11 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXComboBox
|
||||
import javafx.scene.layout.*
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||
import javax.swing.event.ChangeListener
|
||||
|
||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
val versionsPane = VBox()
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.util.Log4jLevel
|
||||
import org.jackhuang.hmcl.util.inc
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.readFullyAsString
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Node
|
||||
@@ -90,8 +91,8 @@ class LogWindow : Stage() {
|
||||
|
||||
engine = webView.engine
|
||||
engine.loadContent(javaClass.getResourceAsStream("/assets/log-window-content.html").readFullyAsString().replace("\${FONT}", "${Settings.font.size}px \"${Settings.font.family}\""))
|
||||
engine.loadWorker.stateProperty().addListener { _, _, newValue ->
|
||||
if (newValue == Worker.State.SUCCEEDED) {
|
||||
engine.loadWorker.stateProperty().onChange {
|
||||
if (it == Worker.State.SUCCEEDED) {
|
||||
document = engine.document
|
||||
body = document.getElementsByTagName("body").item(0)
|
||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||
@@ -105,8 +106,8 @@ class LogWindow : Stage() {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
cboLines.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
|
||||
Settings.logLines = newValue.toInt()
|
||||
cboLines.selectionModel.selectedItemProperty().onChange {
|
||||
Settings.logLines = it?.toInt() ?: 100
|
||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||
}
|
||||
if (!flag) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
/**
|
||||
* @see /assets/fxml/main.fxml
|
||||
@@ -104,9 +105,9 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
profile.repository.getVersions().forEach { version ->
|
||||
children += buildNode(++i, profile, version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown", group)
|
||||
}
|
||||
group.selectedToggleProperty().addListener { _, _, newValue ->
|
||||
if (newValue != null)
|
||||
profile.selectedVersion = newValue.properties["version"] as String
|
||||
group.selectedToggleProperty().onChange {
|
||||
if (it != null)
|
||||
profile.selectedVersion = it.properties["version"] as String
|
||||
}
|
||||
masonryPane.resetChildren(children)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.mod.ModManager
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.task
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class ModController {
|
||||
@FXML lateinit var scrollPane: ScrollPane
|
||||
@@ -71,8 +72,8 @@ class ModController {
|
||||
JFXDepthManager.setDepth(this, 1)
|
||||
style += "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
||||
|
||||
modInfo.activeProperty.addListener { _, _, newValue ->
|
||||
if (newValue)
|
||||
modInfo.activeProperty.onChange {
|
||||
if (it)
|
||||
styleClass -= "disabled"
|
||||
else
|
||||
styleClass += "disabled"
|
||||
|
||||
@@ -22,6 +22,7 @@ import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.BorderPane
|
||||
import org.jackhuang.hmcl.mod.ModInfo
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : BorderPane() {
|
||||
@FXML lateinit var lblModFileName: Label
|
||||
@@ -34,8 +35,8 @@ class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : Bo
|
||||
lblModFileName.text = info.fileName
|
||||
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.mcversion}, Authors: ${info.authors}"
|
||||
chkEnabled.isSelected = info.isActive
|
||||
chkEnabled.selectedProperty().addListener { _, _, newValue ->
|
||||
info.activeProperty.set(newValue)
|
||||
chkEnabled.selectedProperty().onChange {
|
||||
info.activeProperty.set(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import javafx.fxml.FXMLLoader
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.shape.SVGPath
|
||||
import kotlin.coroutines.experimental.EmptyCoroutineContext.plus
|
||||
|
||||
object SVG {
|
||||
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.Validator
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class SettingsPage : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.launcher"))
|
||||
@@ -59,41 +60,33 @@ class SettingsPage : StackPane(), DecoratorPage {
|
||||
cboDownloadSource.limitWidth(400.0)
|
||||
|
||||
txtProxyHost.text = Settings.proxyHost
|
||||
txtProxyHost.textProperty().addListener { _, _, newValue ->
|
||||
Settings.proxyHost = newValue
|
||||
}
|
||||
txtProxyHost.textProperty().onChange { Settings.proxyHost = it }
|
||||
|
||||
txtProxyPort.text = Settings.proxyPort
|
||||
txtProxyPort.textProperty().addListener { _, _, newValue ->
|
||||
Settings.proxyPort = newValue
|
||||
}
|
||||
txtProxyPort.textProperty().onChange { Settings.proxyPort = it }
|
||||
|
||||
txtProxyUsername.text = Settings.proxyUser
|
||||
txtProxyUsername.textProperty().addListener { _, _, newValue ->
|
||||
Settings.proxyUser = newValue
|
||||
}
|
||||
txtProxyUsername.textProperty().onChange { Settings.proxyUser = it }
|
||||
|
||||
txtProxyPassword.text = Settings.proxyPass
|
||||
txtProxyPassword.textProperty().addListener { _, _, newValue ->
|
||||
Settings.proxyPass = newValue
|
||||
}
|
||||
txtProxyPassword.textProperty().onChange { Settings.proxyPass = it }
|
||||
|
||||
cboDownloadSource.selectionModel.select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.downloadProvider))
|
||||
cboDownloadSource.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||
Settings.downloadProvider = DownloadProviders.getDownloadProvider(newValue.toInt())
|
||||
cboDownloadSource.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.downloadProvider = DownloadProviders.getDownloadProvider(it)
|
||||
}
|
||||
|
||||
cboFont.selectionModel.select(Settings.font.family)
|
||||
cboFont.valueProperty().addListener { _, _, newValue ->
|
||||
val font = Font.font(newValue, Settings.font.size)
|
||||
cboFont.valueProperty().onChange {
|
||||
val font = Font.font(it, Settings.font.size)
|
||||
Settings.font = font
|
||||
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${font.family}\";"
|
||||
}
|
||||
txtFontSize.text = Settings.font.size.toString()
|
||||
txtFontSize.validators += Validator { it.toDoubleOrNull() != null }
|
||||
txtFontSize.textProperty().addListener { _, _, newValue ->
|
||||
txtFontSize.textProperty().onChange {
|
||||
if (txtFontSize.validate()) {
|
||||
val font = Font.font(Settings.font.family, newValue.toDouble())
|
||||
val font = Font.font(Settings.font.family, it!!.toDouble())
|
||||
Settings.font = font
|
||||
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.font.family}\";"
|
||||
}
|
||||
@@ -106,13 +99,13 @@ class SettingsPage : StackPane(), DecoratorPage {
|
||||
}
|
||||
cboLanguage.items = list
|
||||
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.locale))
|
||||
cboLanguage.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||
Settings.locale = Locales.getLocale(newValue.toInt())
|
||||
cboLanguage.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.locale = Locales.getLocale(it)
|
||||
}
|
||||
|
||||
cboProxyType.selectionModel.select(Proxies.PROXIES.indexOf(Settings.proxyType))
|
||||
cboProxyType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||
Settings.proxyType = Proxies.getProxyType(newValue.toInt())
|
||||
cboProxyType.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.proxyType = Proxies.getProxyType(it)
|
||||
}
|
||||
|
||||
fileCommonLocation.setProperty(Settings.commonPathProperty)
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXCheckBox
|
||||
import com.jfoenix.controls.JFXRadioButton
|
||||
import com.jfoenix.effects.JFXDepthManager
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
|
||||
@@ -20,10 +20,7 @@ package org.jackhuang.hmcl.ui
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Button
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.Tooltip
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
|
||||
class VersionListItem(val versionName: String, val gameVersion: String) : StackPane() {
|
||||
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
*/
|
||||
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.StringProperty
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Alert
|
||||
import javafx.scene.layout.*
|
||||
import org.jackhuang.hmcl.download.GameAssetIndexDownloadTask
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
|
||||
@@ -19,15 +19,15 @@ package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.value.ChangeListener
|
||||
import javafx.fxml.FXML
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.control.Toggle
|
||||
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 org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
|
||||
@@ -26,9 +26,7 @@ import javafx.scene.SnapshotParameters
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.image.WritableImage
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.shape.Rectangle
|
||||
import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.ui.setOverflowHidden
|
||||
import org.jackhuang.hmcl.ui.takeSnapshot
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import javafx.beans.DefaultProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ListChangeListener
|
||||
@@ -27,7 +26,10 @@ import javafx.collections.ObservableList
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.StackPane
|
||||
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")
|
||||
class ComponentList: StackPane() {
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
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.geometry.Insets
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.HBox
|
||||
import javafx.scene.layout.Priority
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.shape.Rectangle
|
||||
@@ -33,7 +33,9 @@ import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.ui.SINE
|
||||
import org.jackhuang.hmcl.ui.SVG
|
||||
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() {
|
||||
|
||||
@@ -146,8 +148,8 @@ class ComponentListCell(private val content: Node) : StackPane() {
|
||||
expandAnimation?.play()
|
||||
}
|
||||
|
||||
expandedProperty.addListener { _, _, newValue ->
|
||||
if (newValue) {
|
||||
expandedProperty.onChange {
|
||||
if (it) {
|
||||
expandIcon.rotate = 180.0
|
||||
} else {
|
||||
expandIcon.rotate = 0.0
|
||||
|
||||
@@ -27,7 +27,8 @@ import javafx.scene.layout.VBox
|
||||
import javafx.stage.DirectoryChooser
|
||||
import org.jackhuang.hmcl.ui.Controllers
|
||||
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() {
|
||||
val nameProperty = SimpleStringProperty(this, "name")
|
||||
|
||||
@@ -23,12 +23,13 @@ import javafx.collections.FXCollections
|
||||
import javafx.scene.control.ListCell
|
||||
import javafx.scene.text.Font
|
||||
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())) {
|
||||
init {
|
||||
valueProperty().addListener { _, _, newValue ->
|
||||
valueProperty().onChange {
|
||||
if (enableStyle)
|
||||
style = "-fx-font-family: \"$newValue\";"
|
||||
style = "-fx-font-family: \"$it\";"
|
||||
}
|
||||
cellFactory = Callback {
|
||||
object : ListCell<String>() {
|
||||
|
||||
@@ -26,11 +26,15 @@ import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.geometry.Insets
|
||||
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.Paint
|
||||
import javafx.scene.shape.Rectangle
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@@ -92,8 +96,8 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
||||
}
|
||||
|
||||
}
|
||||
focusedProperty().addListener { _, _, newVal ->
|
||||
if (newVal) {
|
||||
focusedProperty().onChange {
|
||||
if (it) {
|
||||
if (!isPressed) {
|
||||
this.buttonRippler.showOverlay()
|
||||
}
|
||||
@@ -125,7 +129,7 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
||||
return@Callable background
|
||||
}
|
||||
}, backgroundProperty()))
|
||||
ripplerFillProperty.addListener { _, _, newVal -> this.buttonRippler.ripplerFill = newVal }
|
||||
ripplerFillProperty.onChange { this.buttonRippler.ripplerFill = it }
|
||||
if (background == null || this.isJavaDefaultBackground(background)) {
|
||||
background = Background(BackgroundFill(Color.TRANSPARENT, this.defaultRadii, null))
|
||||
}
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
*/
|
||||
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.InputStream
|
||||
import java.util.Locale
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
|
||||
object UTF8Control : ResourceBundle.Control() {
|
||||
@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.wizard.WizardController
|
||||
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 {
|
||||
|
||||
@@ -42,9 +43,9 @@ class InstallersPage(private val controller: WizardController, private val downl
|
||||
val gameVersion = controller.settings["game"] as String
|
||||
txtName.text = gameVersion
|
||||
|
||||
list.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||
controller.settings[INSTALLER_TYPE] = newValue
|
||||
controller.onNext(when (newValue){
|
||||
list.selectionModel.selectedIndexProperty().onChange {
|
||||
controller.settings[INSTALLER_TYPE] = it
|
||||
controller.onNext(when (it){
|
||||
0 -> VersionsPage(controller, gameVersion, downloadProvider, "forge") { controller.onPrev(false) }
|
||||
1 -> VersionsPage(controller, gameVersion, downloadProvider, "liteloader") { 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.mod.readCurseForgeModpackManifest
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.Controllers
|
||||
import org.jackhuang.hmcl.ui.construct.Validator
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
|
||||
@@ -21,8 +21,8 @@ import com.jfoenix.controls.JFXListView
|
||||
import com.jfoenix.controls.JFXSpinner
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.TaskExecutor
|
||||
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.WizardController
|
||||
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 {
|
||||
|
||||
@@ -44,8 +45,8 @@ class VersionsPage(private val controller: WizardController, private val gameVer
|
||||
init {
|
||||
loadFXML("/assets/fxml/download/versions.fxml")
|
||||
children.setAll(spinner)
|
||||
list.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
|
||||
controller.settings[libraryId] = newValue.remoteVersion.selfVersion
|
||||
list.selectionModel.selectedItemProperty().onChange {
|
||||
controller.settings[libraryId] = it!!.remoteVersion.selfVersion
|
||||
callback()
|
||||
}
|
||||
refresh()
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui.wizard
|
||||
import com.jfoenix.concurrency.JFXUtilities
|
||||
import com.jfoenix.controls.JFXProgressBar
|
||||
import javafx.application.Platform
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import com.jfoenix.transitions.hamburger.HamburgerBackArrowBasicTransition?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
@@ -60,7 +59,11 @@
|
||||
<BorderPane minWidth="200" maxWidth="200" fx:id="titleWrapper">
|
||||
<center>
|
||||
<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>
|
||||
<right>
|
||||
<Rectangle height="${navBar.height}" width="1" fill="gray"/>
|
||||
|
||||
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.auth.yggdrasil
|
||||
|
||||
import com.google.gson.*
|
||||
import java.lang.reflect.Type
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.UUID
|
||||
import com.google.gson.JsonParseException
|
||||
import java.util.*
|
||||
|
||||
data class GameProfile(
|
||||
val id: UUID? = null,
|
||||
|
||||
@@ -20,13 +20,6 @@ package org.jackhuang.hmcl.auth.yggdrasil
|
||||
import com.google.gson.*
|
||||
import java.lang.reflect.Type
|
||||
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>() {
|
||||
|
||||
@@ -21,9 +21,6 @@ import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonParseException
|
||||
import org.jackhuang.hmcl.auth.*
|
||||
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.net.Proxy
|
||||
import java.net.URL
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
|
||||
import org.jackhuang.hmcl.game.*
|
||||
import org.jackhuang.hmcl.game.GameRepository
|
||||
import java.net.Proxy
|
||||
|
||||
abstract class AbstractDependencyManager(repository: GameRepository, proxy: Proxy)
|
||||
: DependencyManager(repository, proxy) {
|
||||
abstract class AbstractDependencyManager
|
||||
: DependencyManager {
|
||||
abstract val downloadProvider: DownloadProvider
|
||||
|
||||
fun getVersions(id: String, selfVersion: String) =
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
*/
|
||||
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() {
|
||||
override val libraryBaseURL: String = "http://bmclapi2.bangbang93.com/libraries/"
|
||||
override val versionListURL: String = "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json"
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
*/
|
||||
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.Version
|
||||
import org.jackhuang.hmcl.task.ParallelTask
|
||||
@@ -27,8 +34,8 @@ import java.net.Proxy
|
||||
/**
|
||||
* This class has no state.
|
||||
*/
|
||||
class DefaultDependencyManager(override val repository: DefaultGameRepository, override var downloadProvider: DownloadProvider, proxy: Proxy = Proxy.NO_PROXY)
|
||||
: AbstractDependencyManager(repository, proxy) {
|
||||
class DefaultDependencyManager(override val repository: DefaultGameRepository, override var downloadProvider: DownloadProvider, override val proxy: Proxy = Proxy.NO_PROXY)
|
||||
: AbstractDependencyManager() {
|
||||
|
||||
override fun gameBuilder(): GameBuilder = DefaultGameBuilder(this)
|
||||
|
||||
|
||||
@@ -17,9 +17,16 @@
|
||||
*/
|
||||
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.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.*
|
||||
|
||||
class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameBuilder() {
|
||||
|
||||
@@ -22,27 +22,51 @@ import org.jackhuang.hmcl.game.Version
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
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 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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
abstract fun getVersionList(id: String): VersionList<*>
|
||||
fun getVersionList(id: String): VersionList<*>
|
||||
}
|
||||
@@ -17,14 +17,31 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
|
||||
import org.jackhuang.hmcl.download.VersionList
|
||||
|
||||
/**
|
||||
* The service provider that provides Minecraft online file downloads.
|
||||
*/
|
||||
abstract class DownloadProvider {
|
||||
abstract val libraryBaseURL: String
|
||||
abstract val versionListURL: String
|
||||
abstract val versionBaseURL: String
|
||||
abstract val assetIndexBaseURL: 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
|
||||
|
||||
/**
|
||||
* 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<*>
|
||||
}
|
||||
@@ -19,12 +19,20 @@ package org.jackhuang.hmcl.download
|
||||
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
|
||||
|
||||
/**
|
||||
* The builder which provide a task to build Minecraft environment.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
abstract class GameBuilder {
|
||||
var name: String = ""
|
||||
protected var gameVersion: 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 {
|
||||
this.name = name
|
||||
return this
|
||||
@@ -35,10 +43,17 @@ abstract class GameBuilder {
|
||||
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 {
|
||||
toolVersions[id] = version
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the task that can build thw whole Minecraft environment
|
||||
*/
|
||||
abstract fun buildAsync(): Task
|
||||
}
|
||||
@@ -17,8 +17,15 @@
|
||||
*/
|
||||
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.*
|
||||
|
||||
/**
|
||||
* @see {@link http://wiki.vg}
|
||||
*/
|
||||
object MojangDownloadProvider : DownloadProvider() {
|
||||
override val libraryBaseURL: String = "https://libraries.minecraft.net/"
|
||||
override val versionBaseURL: String = "http://s3.amazonaws.com/Minecraft.Download/versions/"
|
||||
@@ -37,15 +44,6 @@ object MojangDownloadProvider : DownloadProvider() {
|
||||
}
|
||||
|
||||
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"))
|
||||
return baseURL
|
||||
else if (Locale.getDefault() == Locale.CHINA)
|
||||
|
||||
@@ -19,14 +19,18 @@ package org.jackhuang.hmcl.download
|
||||
|
||||
import org.jackhuang.hmcl.util.VersionNumber
|
||||
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> (
|
||||
val gameVersion: String,
|
||||
val selfVersion: String,
|
||||
/**
|
||||
* The file of remote version, may be an installer or an universal jar.
|
||||
*/
|
||||
val url: String,
|
||||
val tag: T
|
||||
): Comparable<RemoteVersion<T>> {
|
||||
@@ -39,6 +43,7 @@ data class RemoteVersion<T> (
|
||||
}
|
||||
|
||||
override fun compareTo(other: RemoteVersion<T>): Int {
|
||||
// newer versions are smaller than older versions
|
||||
return -selfVersion.compareTo(other.selfVersion)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,20 +22,37 @@ import org.jackhuang.hmcl.util.SimpleMultimap
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
* The remote version list.
|
||||
* @param T The type of RemoteVersion<T>, the type of tags.
|
||||
*/
|
||||
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)
|
||||
|
||||
/**
|
||||
* True if the version list has been loaded.
|
||||
*/
|
||||
val loaded = versions.isNotEmpty
|
||||
|
||||
/**
|
||||
* @param downloadProvider DownloadProvider
|
||||
* @return the task to reload the remote version list.
|
||||
*/
|
||||
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]
|
||||
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
|
||||
* @return the collection of specific remote versions
|
||||
*/
|
||||
@@ -43,6 +60,13 @@ abstract class VersionList<T> {
|
||||
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>? {
|
||||
var result : RemoteVersion<T>? = null
|
||||
versions[gameVersion].forEach {
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.SimpleVersionProvider
|
||||
import org.jackhuang.hmcl.game.Version
|
||||
@@ -30,9 +33,9 @@ import java.io.IOException
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String) : TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String) : TaskResult<Version>() {
|
||||
private val forgeVersionList = dependencyManager.getVersionList("forge")
|
||||
private val installer: File = File("forge-installer.jar").absoluteFile
|
||||
lateinit var remote: RemoteVersion<*>
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
package org.jackhuang.hmcl.download.forge
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.game.Version
|
||||
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.Task
|
||||
import org.jackhuang.hmcl.util.*
|
||||
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.AssetObject
|
||||
import org.jackhuang.hmcl.game.DownloadType
|
||||
@@ -30,13 +33,12 @@ import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
|
||||
/**
|
||||
* This task is to download game libraries.
|
||||
* This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task)
|
||||
* @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 fun execute() {
|
||||
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 fun execute() {
|
||||
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() {
|
||||
override val dependencies = LinkedList<Task>()
|
||||
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>>>() {
|
||||
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
|
||||
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() {
|
||||
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
|
||||
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() {
|
||||
override fun execute() {
|
||||
val json = dependencyManager.repository.getVersionJson(version.id).absoluteFile
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
package org.jackhuang.hmcl.download.game
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.game.ReleaseType
|
||||
@@ -15,13 +15,16 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.asVersion
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
import org.jackhuang.hmcl.task.GetTask
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import org.jackhuang.hmcl.util.toURL
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.Library
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
||||
@@ -27,12 +30,12 @@ import org.jackhuang.hmcl.task.then
|
||||
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,
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val liteLoaderVersionList = dependencyManager.getVersionList("liteloader") as LiteLoaderVersionList
|
||||
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
||||
override val dependents = mutableListOf<Task>()
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
package org.jackhuang.hmcl.download.liteloader
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.game.Library
|
||||
@@ -15,13 +15,16 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.asVersion
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
import org.jackhuang.hmcl.task.GetTask
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import org.jackhuang.hmcl.util.toURL
|
||||
|
||||
object LiteLoaderVersionList : VersionList<LiteLoaderRemoteVersionTag>() {
|
||||
@@ -15,15 +15,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.Task
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.asVersion
|
||||
import org.jackhuang.hmcl.util.toURL
|
||||
import org.jackhuang.hmcl.util.typeOf
|
||||
import java.util.TreeSet
|
||||
|
||||
object OptiFineBMCLVersionList : VersionList<Unit>() {
|
||||
override fun refreshAsync(downloadProvider: DownloadProvider): Task {
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
package org.jackhuang.hmcl.download.optifine
|
||||
|
||||
|
||||
object Opt
|
||||
@@ -15,8 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.Library
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo
|
||||
@@ -26,10 +29,13 @@ import org.jackhuang.hmcl.task.TaskResult
|
||||
import org.jackhuang.hmcl.task.then
|
||||
import org.jackhuang.hmcl.util.merge
|
||||
|
||||
/**
|
||||
* **Note**: OptiFine should be installed in the end.
|
||||
*/
|
||||
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val optiFineVersionList = dependencyManager.getVersionList("optifine")
|
||||
lateinit var remote: RemoteVersion<*>
|
||||
override val dependents = mutableListOf<Task>()
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download
|
||||
package org.jackhuang.hmcl.download.optifine
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@@ -15,15 +15,18 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.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.Task
|
||||
import org.jackhuang.hmcl.util.toURL
|
||||
import org.w3c.dom.Element
|
||||
import java.io.ByteArrayInputStream
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import java.util.regex.Pattern
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
object OptiFineVersionList : VersionList<Unit>() {
|
||||
private val pattern = Pattern.compile("OptiFine (.*?) ")
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.event
|
||||
|
||||
import org.jackhuang.hmcl.util.JavaProcess
|
||||
import java.util.EventObject
|
||||
import org.jackhuang.hmcl.util.ManagedProcess
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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.
|
||||
@@ -69,7 +69,7 @@ class JavaProcessExitedAbnormallyEvent(source: Any, val value: JavaProcess) : Ev
|
||||
* @param JavaProcess minecraft process
|
||||
* @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.
|
||||
@@ -79,4 +79,4 @@ class JavaProcessStoppedEvent(source: Any, val value: JavaProcess) : EventObject
|
||||
* @param JavaProcess the crashed process.
|
||||
* @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
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable
|
||||
import java.net.URL
|
||||
|
||||
@Immutable
|
||||
class AssetIndexInfo(
|
||||
|
||||
@@ -20,6 +20,9 @@ package org.jackhuang.hmcl.game
|
||||
import org.jackhuang.hmcl.util.Immutable
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* The Minecraft version for 1.5.x and earlier.
|
||||
*/
|
||||
@Immutable
|
||||
class ClassicVersion : Version(
|
||||
mainClass = "net.minecraft.client.Minecraft",
|
||||
@@ -41,6 +44,9 @@ class ClassicVersion : Version(
|
||||
)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Check if this directory is an old style Minecraft repository.
|
||||
*/
|
||||
fun hasClassicVersion(baseDirectory: File): Boolean {
|
||||
val file = File(baseDirectory, "bin")
|
||||
if (!file.exists()) return false
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
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.LOG
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
@@ -28,6 +31,9 @@ import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
/**
|
||||
* An implementation of classic Minecraft game repository.
|
||||
*/
|
||||
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
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
|
||||
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 getVersionRoot(id: String) = File(baseDirectory, "versions/$id")
|
||||
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() {
|
||||
|
||||
versions.clear()
|
||||
|
||||
if (ClassicVersion.hasClassicVersion(baseDirectory)) {
|
||||
@@ -167,7 +177,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
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")
|
||||
|
||||
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)
|
||||
protected open fun reconstructAssets(version: String, assetId: String): File {
|
||||
val assetsDir = getAssetDirectory(version, assetId)
|
||||
val assetVersion = assetId
|
||||
val indexFile: File = getIndexFile(version, assetVersion)
|
||||
val virtualRoot = assetsDir.resolve("virtual").resolve(assetVersion)
|
||||
val indexFile: File = getIndexFile(version, assetId)
|
||||
val virtualRoot = assetsDir.resolve("virtual").resolve(assetId)
|
||||
|
||||
if (!indexFile.isFile) {
|
||||
return assetsDir
|
||||
|
||||
@@ -18,13 +18,15 @@
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* Does the version of id exist?
|
||||
* @param id the id of version
|
||||
@@ -126,7 +128,8 @@ interface GameRepository : VersionProvider {
|
||||
* Will reconstruct assets or do some blocking tasks if necessary.
|
||||
* 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.
|
||||
* @return the actual asset directory
|
||||
*/
|
||||
@@ -134,24 +137,31 @@ interface GameRepository : VersionProvider {
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Get the file that given asset object refers to
|
||||
*
|
||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
||||
* @param name the asset object name, [AssetIndex.objects.key]
|
||||
* @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]
|
||||
* @param name the asset object name, you can find it in [AssetIndex.objects.keys]
|
||||
* @throws java.io.IOException if I/O operation fails.
|
||||
* @return the file that given asset object refers to
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun getAssetObject(version: String, assetId: String, name: String): File
|
||||
|
||||
/**
|
||||
* Get the file that given asset object refers to
|
||||
*
|
||||
* @param assetId the asset id, [AssetIndexInfo.id] [Version.assets]
|
||||
* @param obj the asset object, [AssetIndex.objects]
|
||||
* @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]
|
||||
* @param obj the asset object, you can find it in [AssetIndex.objects]
|
||||
* @return the file that given asset object refers to
|
||||
*/
|
||||
fun getAssetObject(version: String, assetId: String, obj: AssetObject): File
|
||||
@@ -159,17 +169,28 @@ interface GameRepository : VersionProvider {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @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
|
||||
* @return the file that loggingInfo refers to
|
||||
*/
|
||||
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.readFullyAsByteArray
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.io.File
|
||||
|
||||
private fun lessThan32(b: ByteArray, startIndex: Int): Int {
|
||||
for (i in startIndex until b.size)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.util.Immutable
|
||||
import org.jackhuang.hmcl.util.Validation
|
||||
|
||||
@@ -23,6 +23,7 @@ package org.jackhuang.hmcl.game
|
||||
* @see Version.resolve
|
||||
*/
|
||||
interface VersionProvider {
|
||||
|
||||
/**
|
||||
* Does the version of id exist?
|
||||
* @param id the id of version
|
||||
@@ -36,4 +37,5 @@ interface VersionProvider {
|
||||
* @return the version you want
|
||||
*/
|
||||
fun getVersion(id: String): Version
|
||||
|
||||
}
|
||||
@@ -18,14 +18,16 @@
|
||||
package org.jackhuang.hmcl.launch
|
||||
|
||||
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.util.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.coroutines.experimental.EmptyCoroutineContext.plus
|
||||
|
||||
/**
|
||||
* @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
|
||||
val builder = ProcessBuilder(rawCommandLine)
|
||||
@@ -237,7 +239,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
||||
|
||||
builder.directory(repository.getRunDirectory(version.id))
|
||||
.environment().put("APPDATA", options.gameDir.absoluteFile.parent)
|
||||
val p = JavaProcess(builder.start(), rawCommandLine)
|
||||
val p = ManagedProcess(builder.start(), rawCommandLine)
|
||||
if (listener == null)
|
||||
startMonitors(p)
|
||||
else
|
||||
@@ -245,8 +247,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
||||
return p
|
||||
}
|
||||
|
||||
fun launchAsync(): TaskResult<JavaProcess> {
|
||||
return object : TaskResult<JavaProcess>() {
|
||||
fun launchAsync(): TaskResult<ManagedProcess> {
|
||||
return object : TaskResult<ManagedProcess>() {
|
||||
override val id = LAUNCH_ASYNC_ID
|
||||
override fun execute() {
|
||||
result = launch()
|
||||
@@ -279,20 +281,20 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
||||
return scriptFile
|
||||
}
|
||||
|
||||
private fun startMonitors(javaProcess: JavaProcess) {
|
||||
javaProcess.relatedThreads += thread(name = "stdout-pump", isDaemon = true, block = StreamPump(javaProcess.process.inputStream)::run)
|
||||
javaProcess.relatedThreads += thread(name = "stderr-pump", isDaemon = true, block = StreamPump(javaProcess.process.errorStream)::run)
|
||||
private fun startMonitors(managedProcess: ManagedProcess) {
|
||||
managedProcess.relatedThreads += thread(name = "stdout-pump", isDaemon = true, block = StreamPump(managedProcess.process.inputStream)::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) {
|
||||
processListener.setProcess(javaProcess)
|
||||
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); javaProcess.lines += line }.apply { start() }
|
||||
javaProcess.relatedThreads += logHandler
|
||||
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.inputStream, { logHandler.newLine(it) } )::run)
|
||||
javaProcess.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)
|
||||
javaProcess.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)
|
||||
private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
||||
processListener.setProcess(managedProcess)
|
||||
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() }
|
||||
managedProcess.relatedThreads += logHandler
|
||||
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.inputStream, { logHandler.newLine(it) } )::run)
|
||||
managedProcess.relatedThreads += stdout
|
||||
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)
|
||||
managedProcess.relatedThreads += stderr
|
||||
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 {
|
||||
|
||||
@@ -21,16 +21,15 @@ import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.event.JVMLaunchFailedEvent
|
||||
import org.jackhuang.hmcl.event.JavaProcessExitedAbnormallyEvent
|
||||
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.guessLogLineError
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @param process the process to wait for
|
||||
* @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() {
|
||||
try {
|
||||
process.process.waitFor()
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jackhuang.hmcl.auth.AuthInfo
|
||||
import org.jackhuang.hmcl.game.GameRepository
|
||||
import org.jackhuang.hmcl.game.LaunchOptions
|
||||
import org.jackhuang.hmcl.game.Version
|
||||
import org.jackhuang.hmcl.util.JavaProcess
|
||||
import org.jackhuang.hmcl.util.ManagedProcess
|
||||
import java.io.File
|
||||
|
||||
abstract class Launcher(
|
||||
@@ -34,7 +34,7 @@ abstract class Launcher(
|
||||
|
||||
val version: Version = repository.getVersion(versionId).resolve(repository)
|
||||
abstract val rawCommandLine: List<String>
|
||||
abstract fun launch(): JavaProcess
|
||||
abstract fun launch(): ManagedProcess
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* Also supports plain logs.
|
||||
*/
|
||||
internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread() {
|
||||
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() {
|
||||
if (interrupted.get())
|
||||
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) =
|
||||
Scheduler.COMPUTATION.schedule {
|
||||
@@ -102,6 +106,7 @@ internal class Log4jHandler(val callback: (String, Log4jLevel) -> Unit) : Thread
|
||||
l = Log4jLevel.ERROR
|
||||
}
|
||||
"log4j_Message" -> readingMessage = true
|
||||
"log4j_Throwable" -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.launch
|
||||
|
||||
import org.jackhuang.hmcl.util.JavaProcess
|
||||
import org.jackhuang.hmcl.util.Log4jLevel
|
||||
import org.jackhuang.hmcl.util.ManagedProcess
|
||||
|
||||
interface ProcessListener {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
fun setProcess(process: JavaProcess) {}
|
||||
fun setProcess(process: ManagedProcess) {}
|
||||
|
||||
/**
|
||||
* Called when receiving a log from stdout/stderr.
|
||||
|
||||
@@ -23,6 +23,12 @@ import java.io.IOException
|
||||
import java.io.InputStream
|
||||
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(
|
||||
private val inputStream: InputStream,
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param f the CurseForge modpack file.
|
||||
* @return the manifest.
|
||||
*/
|
||||
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
||||
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()
|
||||
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() {
|
||||
val repository = dependencyManager.repository
|
||||
init {
|
||||
@@ -136,7 +149,8 @@ class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDepende
|
||||
f.fileName = f.url.detectFileName(dependencyManager.proxy)
|
||||
dependencies += FileDownloadTask(f.url, run.resolve("mods").resolve(f.fileName), proxy = dependencyManager.proxy)
|
||||
} 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
|
||||
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() {
|
||||
val repository = dependencyManager.repository
|
||||
val run = repository.getRunDirectory(version)
|
||||
@@ -161,6 +181,8 @@ class CurseForgeModpackCompletionTask(dependencyManager: DependencyManager, vers
|
||||
else {
|
||||
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) {
|
||||
if (f.fileName.isBlank())
|
||||
dependents += task { f.fileName = f.url.detectFileName(proxy) }
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.jackhuang.hmcl.util.typeOf
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class ForgeModMetadata(
|
||||
class ForgeModMetadata @JvmOverloads internal constructor(
|
||||
@SerializedName("modid")
|
||||
val modId: String = "",
|
||||
val name: String = "",
|
||||
@@ -42,6 +42,9 @@ class ForgeModMetadata(
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Read Forge mod ModInfo.
|
||||
*/
|
||||
fun fromFile(modFile: File): ModInfo {
|
||||
ZipFile(modFile).use {
|
||||
val entry = it.getEntry("mcmod.info") ?: throw JsonParseException("File $modFile is not a Forge mod.")
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
package org.jackhuang.hmcl.mod
|
||||
|
||||
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.util.zip.ZipFile
|
||||
|
||||
class LiteModMetadata (
|
||||
class LiteModMetadata @JvmOverloads internal constructor(
|
||||
val name: String = "",
|
||||
val version: String = "",
|
||||
val mcversion: String = "",
|
||||
@@ -37,6 +39,9 @@ class LiteModMetadata (
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Read LiteLoader mod ModInfo.
|
||||
*/
|
||||
fun fromFile(modFile: File): ModInfo {
|
||||
ZipFile(modFile).use {
|
||||
val entry = it.getEntry("litemod.json")
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod
|
||||
|
||||
import com.google.gson.JsonParseException
|
||||
import org.jackhuang.hmcl.util.property.ImmediateBooleanProperty
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.ImmediateBooleanProperty
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
import java.io.File
|
||||
|
||||
class ModInfo (
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import java.io.File
|
||||
|
||||
class Modpack(val file: File) {
|
||||
|
||||
@@ -19,6 +19,13 @@ package org.jackhuang.hmcl.task
|
||||
|
||||
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() {
|
||||
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.FailedEvent
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
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 org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.RandomAccessFile
|
||||
import java.math.BigInteger
|
||||
import java.net.Proxy
|
||||
import java.net.URL
|
||||
import java.math.BigInteger
|
||||
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() {
|
||||
override val scheduler: Scheduler = Scheduler.IO
|
||||
|
||||
/**
|
||||
* Once downloading fails, this event will be fired to gain the substitute URL.
|
||||
*/
|
||||
var onFailed = EventManager<FailedEvent<URL>>()
|
||||
|
||||
private var rFile: RandomAccessFile? = null
|
||||
private var stream: InputStream? = null
|
||||
|
||||
fun closeFiles() {
|
||||
private fun closeFiles() {
|
||||
rFile?.closeQuietly()
|
||||
rFile = null
|
||||
stream?.closeQuietly()
|
||||
|
||||
@@ -25,6 +25,17 @@ import java.net.Proxy
|
||||
import java.net.URL
|
||||
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>() {
|
||||
override val scheduler: Scheduler = Scheduler.IO
|
||||
|
||||
@@ -65,6 +76,9 @@ class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Ch
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The default task result ID.
|
||||
*/
|
||||
const val ID = "http_get"
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,11 @@
|
||||
*/
|
||||
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() {
|
||||
override val hidden: Boolean = true
|
||||
override val dependents: Collection<Task> = listOf(*tasks)
|
||||
|
||||
@@ -17,13 +17,25 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task
|
||||
|
||||
import javafx.application.Platform
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
/**
|
||||
* Determines how a task is executed.
|
||||
*
|
||||
* @see [Task.scheduler]
|
||||
*/
|
||||
interface Scheduler {
|
||||
/**
|
||||
* Schedules the given task.
|
||||
* @return the future, null if future is not supported.
|
||||
*/
|
||||
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<*>?
|
||||
|
||||
private class SchedulerImpl(val executor: (Runnable) -> Unit) : Scheduler {
|
||||
@@ -58,7 +70,7 @@ interface 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 {
|
||||
@@ -90,13 +102,44 @@ interface Scheduler {
|
||||
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)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* The default scheduler for tasks to be executed.
|
||||
* @see [Task.scheduler]
|
||||
*/
|
||||
val DEFAULT = NEW_THREAD
|
||||
|
||||
/**
|
||||
* Shut down all executor services to guarantee that the application can stop implicitly.
|
||||
*/
|
||||
fun shutdown() {
|
||||
CACHED_EXECUTOR.shutdown()
|
||||
IO_EXECUTOR.shutdown()
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task
|
||||
|
||||
class SilentException : Exception {
|
||||
constructor() : super() {}
|
||||
constructor(message: String) : super(message) {}
|
||||
constructor(message: String, cause: Throwable) : super(message, cause) {}
|
||||
}
|
||||
/**
|
||||
* If a task throws [SilentException], the task will be marked as failure but do not log the stacktrace.
|
||||
*/
|
||||
class SilentException : Exception()
|
||||
@@ -22,12 +22,15 @@ import javafx.beans.property.ReadOnlyDoubleWrapper
|
||||
import javafx.beans.property.ReadOnlyStringProperty
|
||||
import javafx.beans.property.ReadOnlyStringWrapper
|
||||
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.atomic.AtomicReference
|
||||
|
||||
/**
|
||||
* Disposable task.
|
||||
*
|
||||
* @see [TaskExecutor]
|
||||
*/
|
||||
abstract class Task {
|
||||
/**
|
||||
|
||||
@@ -19,11 +19,13 @@ package org.jackhuang.hmcl.task
|
||||
|
||||
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||
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.AtomicInteger
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class TaskExecutor() {
|
||||
var taskListener: TaskListener? = null
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task
|
||||
|
||||
/**
|
||||
* A task that has a result.
|
||||
*/
|
||||
abstract class TaskResult<V> : Task() {
|
||||
open var result: V? = null
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
/**
|
||||
* Mark if the model is immutable.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Immutable
|
||||
@@ -17,6 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
/**
|
||||
* A map that support auto casting.
|
||||
*/
|
||||
class AutoTypingMap<K>(private val impl: MutableMap<K, Any>) {
|
||||
|
||||
fun clear() = impl.clear()
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@@ -20,15 +20,12 @@ package org.jackhuang.hmcl.util
|
||||
import com.google.gson.*
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import java.lang.reflect.Type
|
||||
import com.google.gson.stream.JsonToken
|
||||
import java.io.IOException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.jackhuang.hmcl.game.Library
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Type
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
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 {
|
||||
val cmdbuf = StringBuilder(120)
|
||||
for (i in cmd.indices) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.property
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import javafx.beans.property.*
|
||||
import javafx.beans.value.ChangeListener
|
||||
@@ -24,12 +24,26 @@ import java.io.Serializable
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Represents a Java installation.
|
||||
*/
|
||||
data class JavaVersion internal constructor(
|
||||
@SerializedName("location")
|
||||
val binary: File,
|
||||
val longVersion: String,
|
||||
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)
|
||||
|
||||
companion object {
|
||||
@@ -110,10 +124,12 @@ data class JavaVersion internal constructor(
|
||||
return path.resolve("java")
|
||||
}
|
||||
|
||||
private val currentJava: JavaVersion = JavaVersion(
|
||||
binary = getJavaFile(File(System.getProperty("java.home"))),
|
||||
longVersion = System.getProperty("java.version"),
|
||||
platform = Platform.PLATFORM)
|
||||
private val currentJava: JavaVersion by lazy {
|
||||
JavaVersion(
|
||||
binary = getJavaFile(File(System.getProperty("java.home"))),
|
||||
longVersion = System.getProperty("java.version"),
|
||||
platform = Platform.PLATFORM)
|
||||
}
|
||||
fun fromCurrentEnvironment() = currentJava
|
||||
|
||||
init {
|
||||
|
||||
@@ -18,12 +18,9 @@
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import javafx.beans.property.Property
|
||||
import javafx.beans.value.ObservableValue
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.HashMap
|
||||
import sun.text.normalizer.UTF16.append
|
||||
import java.lang.reflect.Array.getLength
|
||||
|
||||
inline fun ignoreException(func: () -> Unit) {
|
||||
try {
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
@file:JvmName("HMCLog")
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import javafx.scene.paint.Color
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintWriter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.logging.*
|
||||
import java.util.logging.Formatter
|
||||
import javafx.scene.paint.Color
|
||||
import java.util.regex.Pattern
|
||||
|
||||
val LOG = Logger.getLogger("HMCL").apply {
|
||||
|
||||
@@ -20,25 +20,58 @@ package org.jackhuang.hmcl.util
|
||||
import java.util.*
|
||||
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 commands: List<String>
|
||||
) {
|
||||
/**
|
||||
* To save some information you need.
|
||||
*/
|
||||
val properties = mutableMapOf<String, Any>()
|
||||
|
||||
/**
|
||||
* The standard output/error lines.
|
||||
*/
|
||||
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>()
|
||||
|
||||
/**
|
||||
* True if the managed process is running.
|
||||
*/
|
||||
val isRunning: Boolean = try {
|
||||
process.exitValue()
|
||||
true
|
||||
} catch (ex: IllegalThreadStateException) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* The exit code of raw process.
|
||||
*/
|
||||
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() {
|
||||
process.destroy()
|
||||
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.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.Proxy
|
||||
import java.net.URL
|
||||
import java.security.GeneralSecurityException
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
import javax.net.ssl.HostnameVerifier
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import javax.net.ssl.SSLContext
|
||||
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 {
|
||||
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