Turn to java
This commit is contained in:
@@ -22,11 +22,13 @@ import javafx.application.Platform
|
|||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.ui.Controllers
|
import org.jackhuang.hmcl.ui.Controllers
|
||||||
import org.jackhuang.hmcl.ui.runOnUiThread
|
import org.jackhuang.hmcl.ui.runOnUiThread
|
||||||
import org.jackhuang.hmcl.util.DEFAULT_USER_AGENT
|
import org.jackhuang.hmcl.util.Constants
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import org.jackhuang.hmcl.util.OS
|
import org.jackhuang.hmcl.util.NetworkUtils
|
||||||
|
import org.jackhuang.hmcl.util.OperatingSystem
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ fun i18n(key: String): String {
|
|||||||
try {
|
try {
|
||||||
return Main.RESOURCE_BUNDLE.getString(key)
|
return Main.RESOURCE_BUNDLE.getString(key)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
LOG.log(Level.WARNING, "Cannot find key $key in resource bundle", e)
|
LOG.log(Level.SEVERE, "Cannot find key $key in resource bundle", e)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,20 +64,21 @@ class Main : Application() {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
DEFAULT_USER_AGENT = { "Hello Minecraft! Launcher" }
|
NetworkUtils.setUserAgentSupplier { "Hello Minecraft! Launcher" }
|
||||||
|
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
|
||||||
|
|
||||||
launch(Main::class.java, *args)
|
launch(Main::class.java, *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getWorkingDirectory(folder: String): File {
|
fun getWorkingDirectory(folder: String): File {
|
||||||
val userhome = System.getProperty("user.home", ".")
|
val userhome = System.getProperty("user.home", ".")
|
||||||
return when (OS.CURRENT_OS) {
|
return when (OperatingSystem.CURRENT_OS) {
|
||||||
OS.LINUX -> File(userhome, ".$folder/")
|
OperatingSystem.LINUX -> File(userhome, ".$folder/")
|
||||||
OS.WINDOWS -> {
|
OperatingSystem.WINDOWS -> {
|
||||||
val appdata: String? = System.getenv("APPDATA")
|
val appdata: String? = System.getenv("APPDATA")
|
||||||
File(appdata ?: userhome, ".$folder/")
|
File(appdata ?: userhome, ".$folder/")
|
||||||
}
|
}
|
||||||
OS.OSX -> File(userhome, "Library/Application Support/" + folder)
|
OperatingSystem.OSX -> File(userhome, "Library/Application Support/" + folder)
|
||||||
else -> File(userhome, "$folder/")
|
else -> File(userhome, "$folder/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +92,7 @@ class Main : Application() {
|
|||||||
|
|
||||||
fun stopWithoutJavaFXPlatform() = runOnUiThread {
|
fun stopWithoutJavaFXPlatform() = runOnUiThread {
|
||||||
Controllers.stage.close()
|
Controllers.stage.close()
|
||||||
Scheduler.shutdown()
|
Schedulers.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
val RESOURCE_BUNDLE = Settings.locale.resourceBundle
|
val RESOURCE_BUNDLE = Settings.locale.resourceBundle
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
|||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask
|
import org.jackhuang.hmcl.task.FileDownloadTask
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.ui.DEFAULT_ICON
|
import org.jackhuang.hmcl.ui.DEFAULT_ICON
|
||||||
import org.jackhuang.hmcl.ui.DialogController
|
import org.jackhuang.hmcl.ui.DialogController
|
||||||
@@ -48,11 +49,13 @@ object AccountHelper {
|
|||||||
SkinLoadTask(account, proxy, true)
|
SkinLoadTask(account, proxy, true)
|
||||||
|
|
||||||
private class SkinLoadTask(val account: YggdrasilAccount, val proxy: Proxy, val refresh: Boolean = false): Task() {
|
private class SkinLoadTask(val account: YggdrasilAccount, val proxy: Proxy, val refresh: Boolean = false): Task() {
|
||||||
override val scheduler = Scheduler.IO
|
|
||||||
override val dependencies = mutableListOf<Task>()
|
override fun getScheduler() = Schedulers.io()
|
||||||
|
private val dependencies = mutableListOf<Task>()
|
||||||
|
override fun getDependencies() = dependencies
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
if (account.canLogIn && (account.selectedProfile == null || refresh))
|
if (account.canLogIn() && (account.selectedProfile == null || refresh))
|
||||||
DialogController.logIn(account)
|
DialogController.logIn(account)
|
||||||
val profile = account.selectedProfile ?: return
|
val profile = account.selectedProfile ?: return
|
||||||
val name = profile.name ?: return
|
val name = profile.name ?: return
|
||||||
@@ -60,7 +63,7 @@ object AccountHelper {
|
|||||||
val file = getSkinFile(name)
|
val file = getSkinFile(name)
|
||||||
if (!refresh && file.exists())
|
if (!refresh && file.exists())
|
||||||
return
|
return
|
||||||
dependencies += FileDownloadTask(url.toURL(), file, proxy = proxy)
|
dependencies += FileDownloadTask(url.toURL(), file, proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import org.jackhuang.hmcl.setting.EnumGameDirectory
|
|||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import org.jackhuang.hmcl.util.fromJson
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -72,7 +72,7 @@ class HMCLGameRepository(val profile: Profile, baseDirectory: File)
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun refreshVersionsImpl() {
|
override fun refreshVersionsImpl() {
|
||||||
Scheduler.NEW_THREAD.schedule {
|
Schedulers.newThread().schedule {
|
||||||
versionSettings.clear()
|
versionSettings.clear()
|
||||||
|
|
||||||
super.refreshVersionsImpl()
|
super.refreshVersionsImpl()
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
import org.jackhuang.hmcl.download.game.VersionJSONSaveTask
|
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||||
import org.jackhuang.hmcl.mod.Modpack
|
import org.jackhuang.hmcl.mod.Modpack
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
@@ -26,6 +27,8 @@ import java.io.File
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.jackhuang.hmcl.task.TaskResult
|
import org.jackhuang.hmcl.task.TaskResult
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.Constants.GSON
|
||||||
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,39 +41,42 @@ import java.util.ArrayList
|
|||||||
*/
|
*/
|
||||||
@Throws(IOException::class, JsonParseException::class)
|
@Throws(IOException::class, JsonParseException::class)
|
||||||
fun readHMCLModpackManifest(f: File): Modpack {
|
fun readHMCLModpackManifest(f: File): Modpack {
|
||||||
val manifestJson = f.readTextZipEntry("modpack.json")
|
val manifestJson = CompressingUtils.readTextZipEntry(f, "modpack.json")
|
||||||
val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.")
|
val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.")
|
||||||
val gameJson = f.readTextZipEntry("minecraft/pack.json")
|
val gameJson = CompressingUtils.readTextZipEntry(f, "minecraft/pack.json")
|
||||||
val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.")
|
val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.")
|
||||||
return if (game.jar == null)
|
return if (game.jar == null)
|
||||||
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
|
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
|
||||||
else manifest.copy(manifest = HMCLModpackManifest)
|
else manifest.setManifest(HMCLModpackManifest)
|
||||||
else manifest.copy(manifest = HMCLModpackManifest, gameVersion = game.jar!!)
|
else manifest.setManifest(HMCLModpackManifest).setGameVersion(game.jar)
|
||||||
}
|
}
|
||||||
|
|
||||||
object HMCLModpackManifest
|
object HMCLModpackManifest
|
||||||
|
|
||||||
class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, modpack: Modpack, private val name: String): Task() {
|
class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, modpack: Modpack, private val version: String): Task() {
|
||||||
private val dependency = profile.dependency
|
private val dependency = profile.dependency
|
||||||
private val repository = profile.repository
|
private val repository = profile.repository
|
||||||
override val dependencies = mutableListOf<Task>()
|
private val dependencies = mutableListOf<Task>()
|
||||||
override val dependents = mutableListOf<Task>()
|
private val dependents = mutableListOf<Task>()
|
||||||
|
|
||||||
|
override fun getDependencies() = dependencies
|
||||||
|
override fun getDependents() = dependents
|
||||||
|
|
||||||
init {
|
init {
|
||||||
check(!repository.hasVersion(name), { "Version $name already exists." })
|
check(!repository.hasVersion(version), { "Version $version already exists." })
|
||||||
val json = zipFile.readTextZipEntry("minecraft/pack.json")
|
val json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json")
|
||||||
var version = GSON.fromJson<Version>(json)!!
|
var version = GSON.fromJson<Version>(json)!!
|
||||||
version = version.copy(jar = null)
|
version = version.setJar(null)
|
||||||
dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync()
|
dependents += dependency.gameBuilder().name(this.version).gameVersion(modpack.gameVersion!!).buildAsync()
|
||||||
dependencies += VersionJSONSaveTask(repository, version) // override the json created by buildAsync()
|
dependencies += VersionJsonSaveTask(repository, version) // override the json created by buildAsync()
|
||||||
|
|
||||||
onDone += { event -> if (event.failed) repository.removeVersionFromDisk(name) }
|
onDone() += { event -> if (event.isFailed) repository.removeVersionFromDisk(this.version) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val run = repository.getRunDirectory(name)
|
private val run = repository.getRunDirectory(version)
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
zipFile.uncompressTo(run, "minecraft/", callback = { it != "minecraft/pack.json" }, ignoreExistentFile = false)
|
CompressingUtils.unzip(zipFile, run, "minecraft/", { it != "minecraft/pack.json" }, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +126,12 @@ class HMCLModpackExportTask @JvmOverloads constructor(
|
|||||||
private val whitelist: List<String>,
|
private val whitelist: List<String>,
|
||||||
private val modpack: Modpack,
|
private val modpack: Modpack,
|
||||||
private val output: File,
|
private val output: File,
|
||||||
override val id: String = ID): TaskResult<ZipEngine>() {
|
private val id: String = ID): TaskResult<ZipEngine>() {
|
||||||
|
|
||||||
|
override fun getId() = id
|
||||||
|
|
||||||
init {
|
init {
|
||||||
onDone += { event -> if (event.failed) output.delete() }
|
onDone() += { event -> if (event.isFailed) output.delete() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
@@ -148,8 +156,8 @@ class HMCLModpackExportTask @JvmOverloads constructor(
|
|||||||
|
|
||||||
val mv = repository.getVersion(version).resolve(repository)
|
val mv = repository.getVersion(version).resolve(repository)
|
||||||
val gameVersion = minecraftVersion(repository.getVersionJar(version)) ?: throw IllegalStateException("Cannot parse the version of $version")
|
val gameVersion = minecraftVersion(repository.getVersionJar(version)) ?: throw IllegalStateException("Cannot parse the version of $version")
|
||||||
zip.putTextFile(GSON.toJson(mv.copy(jar = gameVersion)), "minecraft/pack.json") // Making "jar" to gameVersion is to be compatible with old HMCL.
|
zip.putTextFile(GSON.toJson(mv.setJar(gameVersion)), "minecraft/pack.json") // Making "jar" to gameVersion is to be compatible with old HMCL.
|
||||||
zip.putTextFile(GSON.toJson(modpack.copy(gameVersion = gameVersion)), "modpack.json") // Newer HMCL only reads 'gameVersion' field.
|
zip.putTextFile(GSON.toJson(modpack.setGameVersion(gameVersion)), "modpack.json") // Newer HMCL only reads 'gameVersion' field.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.jackhuang.hmcl.auth.AuthInfo
|
|||||||
import org.jackhuang.hmcl.auth.AuthenticationException
|
import org.jackhuang.hmcl.auth.AuthenticationException
|
||||||
import org.jackhuang.hmcl.launch.DefaultLauncher
|
import org.jackhuang.hmcl.launch.DefaultLauncher
|
||||||
import org.jackhuang.hmcl.launch.ProcessListener
|
import org.jackhuang.hmcl.launch.ProcessListener
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackCompletionTask
|
import org.jackhuang.hmcl.mod.CurseCompletionTask
|
||||||
import org.jackhuang.hmcl.setting.LauncherVisibility
|
import org.jackhuang.hmcl.setting.LauncherVisibility
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
@@ -31,10 +31,10 @@ import org.jackhuang.hmcl.task.*
|
|||||||
import org.jackhuang.hmcl.ui.*
|
import org.jackhuang.hmcl.ui.*
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel
|
import org.jackhuang.hmcl.util.Log4jLevel
|
||||||
import org.jackhuang.hmcl.util.ManagedProcess
|
import org.jackhuang.hmcl.util.ManagedProcess
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
|
||||||
object LauncherHelper {
|
object LauncherHelper {
|
||||||
private val launchingStepsPane = LaunchingStepsPane()
|
private val launchingStepsPane = LaunchingStepsPane()
|
||||||
val PROCESS = ConcurrentLinkedQueue<ManagedProcess>()
|
val PROCESS = ConcurrentLinkedQueue<ManagedProcess>()
|
||||||
@@ -49,13 +49,13 @@ object LauncherHelper {
|
|||||||
val setting = profile.getVersionSetting(selectedVersion)
|
val setting = profile.getVersionSetting(selectedVersion)
|
||||||
|
|
||||||
Controllers.dialog(launchingStepsPane)
|
Controllers.dialog(launchingStepsPane)
|
||||||
task(Scheduler.JAVAFX) { emitStatus(LoadingState.DEPENDENCIES) }
|
task(Schedulers.javafx()) { emitStatus(LoadingState.DEPENDENCIES) }
|
||||||
.then(dependency.checkGameCompletionAsync(version))
|
.then(dependency.checkGameCompletionAsync(version))
|
||||||
|
|
||||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.MODS) })
|
.then(task(Schedulers.javafx()) { emitStatus(LoadingState.MODS) })
|
||||||
.then(CurseForgeModpackCompletionTask(dependency, selectedVersion))
|
.then(CurseCompletionTask(dependency, selectedVersion))
|
||||||
|
|
||||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LOGIN) })
|
.then(task(Schedulers.javafx()) { emitStatus(LoadingState.LOGIN) })
|
||||||
.then(task {
|
.then(task {
|
||||||
try {
|
try {
|
||||||
it["account"] = account.logIn(Settings.proxy)
|
it["account"] = account.logIn(Settings.proxy)
|
||||||
@@ -65,7 +65,7 @@ object LauncherHelper {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LAUNCHING) })
|
.then(task(Schedulers.javafx()) { emitStatus(LoadingState.LAUNCHING) })
|
||||||
.then(task {
|
.then(task {
|
||||||
it["launcher"] = HMCLGameLauncher(
|
it["launcher"] = HMCLGameLauncher(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
@@ -84,12 +84,12 @@ object LauncherHelper {
|
|||||||
|
|
||||||
.executor()
|
.executor()
|
||||||
.apply {
|
.apply {
|
||||||
taskListener = object : TaskListener {
|
taskListener = object : TaskListener() {
|
||||||
var finished = 0
|
var finished = 0
|
||||||
|
|
||||||
override fun onFinished(task: Task) {
|
override fun onFinished(task: Task) {
|
||||||
++finished
|
++finished
|
||||||
runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / totTask.get() }
|
runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / runningTasks }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTerminate() {
|
override fun onTerminate() {
|
||||||
@@ -179,9 +179,14 @@ object LauncherHelper {
|
|||||||
if (exitType != ProcessListener.ExitType.NORMAL && logWindow == null){
|
if (exitType != ProcessListener.ExitType.NORMAL && logWindow == null){
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
LogWindow().apply {
|
LogWindow().apply {
|
||||||
for ((line, level) in logs)
|
|
||||||
logLine(line, level)
|
|
||||||
show()
|
show()
|
||||||
|
Schedulers.newThread().schedule {
|
||||||
|
waitForShown()
|
||||||
|
runOnUiThread {
|
||||||
|
for ((line, level) in logs)
|
||||||
|
logLine(line, level)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,18 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.InstanceConfiguration
|
import org.jackhuang.hmcl.mod.*
|
||||||
import org.jackhuang.hmcl.mod.Modpack
|
|
||||||
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
|
||||||
import org.jackhuang.hmcl.mod.readMMCModpackManifest
|
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.util.toStringOrEmpty
|
import org.jackhuang.hmcl.util.toStringOrEmpty
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun readModpackManifest(f: File): Modpack {
|
fun readModpackManifest(f: File): Modpack {
|
||||||
try {
|
try {
|
||||||
return readCurseForgeModpackManifest(f)
|
return CurseManifest.readCurseForgeModpackManifest(f);
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// ignore it, not a valid CurseForge modpack.
|
// ignore it, not a valid CurseForge modpack.
|
||||||
}
|
}
|
||||||
@@ -44,7 +41,7 @@ fun readModpackManifest(f: File): Modpack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val manifest = readMMCModpackManifest(f)
|
val manifest = MultiMCInstanceConfiguration.readMultiMCModpackManifest(f)
|
||||||
return manifest
|
return manifest
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// ignore it, not a valid MMC modpack.
|
// ignore it, not a valid MMC modpack.
|
||||||
@@ -53,36 +50,36 @@ fun readModpackManifest(f: File): Modpack {
|
|||||||
throw IllegalArgumentException("Modpack file $f is not supported.")
|
throw IllegalArgumentException("Modpack file $f is not supported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun InstanceConfiguration.toVersionSetting(vs: VersionSetting) {
|
fun MultiMCInstanceConfiguration.toVersionSetting(vs: VersionSetting) {
|
||||||
vs.usesGlobal = false
|
vs.usesGlobal = false
|
||||||
vs.gameDirType = EnumGameDirectory.VERSION_FOLDER
|
vs.gameDirType = EnumGameDirectory.VERSION_FOLDER
|
||||||
|
|
||||||
if (overrideJavaLocation) {
|
if (isOverrideJavaLocation) {
|
||||||
vs.javaDir = javaPath.toStringOrEmpty()
|
vs.javaDir = javaPath.toStringOrEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideMemory) {
|
if (isOverrideMemory) {
|
||||||
vs.permSize = permGen.toStringOrEmpty()
|
vs.permSize = permGen.toStringOrEmpty()
|
||||||
if (maxMemory != null)
|
if (maxMemory != null)
|
||||||
vs.maxMemory = maxMemory!!
|
vs.maxMemory = maxMemory!!
|
||||||
vs.minMemory = minMemory
|
vs.minMemory = minMemory
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideCommands) {
|
if (isOverrideCommands) {
|
||||||
vs.wrapper = wrapperCommand.orEmpty()
|
vs.wrapper = wrapperCommand.orEmpty()
|
||||||
vs.precalledCommand = preLaunchCommand.orEmpty()
|
vs.precalledCommand = preLaunchCommand.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideJavaArgs) {
|
if (isOverrideJavaArgs) {
|
||||||
vs.javaArgs = jvmArgs.orEmpty()
|
vs.javaArgs = jvmArgs.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideConsole) {
|
if (isOverrideConsole) {
|
||||||
vs.showLogs = showConsole
|
vs.showLogs = isShowConsole
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideWindow) {
|
if (isOverrideWindow) {
|
||||||
vs.fullscreen = fullscreen
|
vs.fullscreen = isFullscreen
|
||||||
if (width != null)
|
if (width != null)
|
||||||
vs.width = width!!
|
vs.width = width!!
|
||||||
if (height != null)
|
if (height != null)
|
||||||
@@ -90,10 +87,10 @@ fun InstanceConfiguration.toVersionSetting(vs: VersionSetting) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MMCInstallVersionSettingTask(private val profile: Profile, val manifest: InstanceConfiguration, val name: String): Task() {
|
class MMCInstallVersionSettingTask(private val profile: Profile, val manifest: MultiMCInstanceConfiguration, private val version: String): Task() {
|
||||||
override val scheduler = Scheduler.JAVAFX
|
override fun getScheduler() = Schedulers.javafx()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val vs = profile.specializeVersionSetting(name)!!
|
val vs = profile.specializeVersionSetting(version)!!
|
||||||
manifest.toVersionSetting(vs)
|
manifest.toVersionSetting(vs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.setting
|
|||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EventBus
|
||||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
@@ -49,7 +49,7 @@ class Profile(name: String = "Default", initialGameDir: File = File(".minecraft"
|
|||||||
init {
|
init {
|
||||||
gameDirProperty.onChange { repository.changeDirectory(it!!) }
|
gameDirProperty.onChange { repository.changeDirectory(it!!) }
|
||||||
selectedVersionProperty.onInvalidated(this::verifySelectedVersion)
|
selectedVersionProperty.onInvalidated(this::verifySelectedVersion)
|
||||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += { event -> if (event.source == repository) verifySelectedVersion() }
|
EventBus.EVENT_BUS.channel<RefreshedVersionsEvent>().register { event -> if (event.source == repository) verifySelectedVersion() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifySelectedVersion() = runOnUiThread {
|
private fun verifySelectedVersion() = runOnUiThread {
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import org.jackhuang.hmcl.auth.Account
|
|||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EventBus
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
@@ -43,7 +44,7 @@ object Settings {
|
|||||||
val GSON = GsonBuilder()
|
val GSON = GsonBuilder()
|
||||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
||||||
.registerTypeAdapter(Profile::class.java, Profile)
|
.registerTypeAdapter(Profile::class.java, Profile)
|
||||||
.registerTypeAdapter(File::class.java, FileTypeAdapter)
|
.registerTypeAdapter(File::class.java, FileTypeAdapter.INSTANCE)
|
||||||
.setPrettyPrinting().create()
|
.setPrettyPrinting().create()
|
||||||
|
|
||||||
const val DEFAULT_PROFILE = "Default"
|
const val DEFAULT_PROFILE = "Default"
|
||||||
@@ -209,14 +210,14 @@ object Settings {
|
|||||||
|
|
||||||
var downloadProvider: DownloadProvider
|
var downloadProvider: DownloadProvider
|
||||||
get() = when (SETTINGS.downloadtype) {
|
get() = when (SETTINGS.downloadtype) {
|
||||||
0 -> MojangDownloadProvider
|
0 -> MojangDownloadProvider.INSTANCE
|
||||||
1 -> BMCLAPIDownloadProvider
|
1 -> BMCLAPIDownloadProvider.INSTANCE
|
||||||
else -> MojangDownloadProvider
|
else -> MojangDownloadProvider.INSTANCE
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
SETTINGS.downloadtype = when (value) {
|
SETTINGS.downloadtype = when (value) {
|
||||||
MojangDownloadProvider -> 0
|
MojangDownloadProvider.INSTANCE -> 0
|
||||||
BMCLAPIDownloadProvider -> 1
|
BMCLAPIDownloadProvider.INSTANCE -> 1
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,14 +276,14 @@ object Settings {
|
|||||||
get() {
|
get() {
|
||||||
if (!hasProfile(SETTINGS.selectedProfile)) {
|
if (!hasProfile(SETTINGS.selectedProfile)) {
|
||||||
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
||||||
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
Schedulers.computation().schedule { onProfileChanged() }
|
||||||
}
|
}
|
||||||
return getProfile(SETTINGS.selectedProfile)
|
return getProfile(SETTINGS.selectedProfile)
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
if (hasProfile(value.name) && value.name != SETTINGS.selectedProfile) {
|
if (hasProfile(value.name) && value.name != SETTINGS.selectedProfile) {
|
||||||
SETTINGS.selectedProfile = value.name
|
SETTINGS.selectedProfile = value.name
|
||||||
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
Schedulers.computation().schedule { onProfileChanged() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,14 +328,14 @@ object Settings {
|
|||||||
}
|
}
|
||||||
val flag = getProfileMap().remove(ver) != null
|
val flag = getProfileMap().remove(ver) != null
|
||||||
if (flag)
|
if (flag)
|
||||||
Scheduler.COMPUTATION.schedule { onProfileLoading() }
|
Schedulers.computation().schedule { onProfileLoading() }
|
||||||
|
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onProfileChanged() {
|
internal fun onProfileChanged() {
|
||||||
selectedProfile.repository.refreshVersions()
|
selectedProfile.repository.refreshVersions()
|
||||||
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
EventBus.EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -342,7 +343,7 @@ object Settings {
|
|||||||
* Invoked by loading GUI phase.
|
* Invoked by loading GUI phase.
|
||||||
*/
|
*/
|
||||||
fun onProfileLoading() {
|
fun onProfileLoading() {
|
||||||
EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
EventBus.EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
||||||
onProfileChanged()
|
onProfileChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,9 @@ import javafx.scene.text.Font
|
|||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.AccountFactory
|
import org.jackhuang.hmcl.auth.AccountFactory
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
|
import org.jackhuang.hmcl.auth.OfflineAccountFactory
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||||
import org.jackhuang.hmcl.ui.construct.UTF8Control
|
import org.jackhuang.hmcl.ui.construct.UTF8Control
|
||||||
@@ -35,19 +37,18 @@ object Proxies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DownloadProviders {
|
object DownloadProviders {
|
||||||
val DOWNLOAD_PROVIDERS = listOf(MojangDownloadProvider, BMCLAPIDownloadProvider)
|
val DOWNLOAD_PROVIDERS = listOf(MojangDownloadProvider.INSTANCE, BMCLAPIDownloadProvider.INSTANCE)
|
||||||
|
|
||||||
fun getDownloadProvider(index: Int) = DOWNLOAD_PROVIDERS.getOrElse(index, { MojangDownloadProvider })
|
fun getDownloadProvider(index: Int) = DOWNLOAD_PROVIDERS.getOrElse(index, { MojangDownloadProvider.INSTANCE })
|
||||||
}
|
}
|
||||||
|
|
||||||
object Accounts {
|
object Accounts {
|
||||||
val OFFLINE_ACCOUNT_KEY = "offline"
|
val OFFLINE_ACCOUNT_KEY = "offline"
|
||||||
val YGGDRASIL_ACCOUNT_KEY = "yggdrasil"
|
val YGGDRASIL_ACCOUNT_KEY = "yggdrasil"
|
||||||
|
|
||||||
val ACCOUNTS = listOf(OfflineAccount, YggdrasilAccount)
|
|
||||||
val ACCOUNT_FACTORY = mapOf<String, AccountFactory<*>>(
|
val ACCOUNT_FACTORY = mapOf<String, AccountFactory<*>>(
|
||||||
OFFLINE_ACCOUNT_KEY to OfflineAccount,
|
OFFLINE_ACCOUNT_KEY to OfflineAccountFactory.INSTANCE,
|
||||||
YGGDRASIL_ACCOUNT_KEY to YggdrasilAccount
|
YGGDRASIL_ACCOUNT_KEY to YggdrasilAccountFactory.INSTANCE
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getAccountType(account: Account): String {
|
fun getAccountType(account: Account): String {
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import com.google.gson.*
|
|||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
|
import org.jackhuang.hmcl.setting.Settings.proxyHost
|
||||||
|
import org.jackhuang.hmcl.setting.Settings.proxyPass
|
||||||
|
import org.jackhuang.hmcl.setting.Settings.proxyPort
|
||||||
|
import org.jackhuang.hmcl.setting.Settings.proxyUser
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -70,7 +74,7 @@ class VersionSetting() {
|
|||||||
/**
|
/**
|
||||||
* The maximum memory that JVM can allocate for heap.
|
* The maximum memory that JVM can allocate for heap.
|
||||||
*/
|
*/
|
||||||
val maxMemoryProperty = ImmediateIntegerProperty(this, "maxMemory", OS.SUGGESTED_MEMORY)
|
val maxMemoryProperty = ImmediateIntegerProperty(this, "maxMemory", OperatingSystem.SUGGESTED_MEMORY.toInt())
|
||||||
var maxMemory: Int by maxMemoryProperty
|
var maxMemory: Int by maxMemoryProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,28 +236,28 @@ class VersionSetting() {
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun toLaunchOptions(gameDir: File): LaunchOptions {
|
fun toLaunchOptions(gameDir: File): LaunchOptions {
|
||||||
return LaunchOptions(
|
return LaunchOptions.Builder()
|
||||||
gameDir = gameDir,
|
.setGameDir(gameDir)
|
||||||
java = javaVersion ?: JavaVersion.fromCurrentEnvironment(),
|
.setJava(javaVersion ?: JavaVersion.fromCurrentEnvironment())
|
||||||
versionName = Main.TITLE,
|
.setVersionName(Main.TITLE)
|
||||||
profileName = Main.TITLE,
|
.setProfileName(Main.TITLE)
|
||||||
minecraftArgs = minecraftArgs,
|
.setMinecraftArgs(minecraftArgs)
|
||||||
javaArgs = javaArgs,
|
.setJavaArgs(javaArgs)
|
||||||
maxMemory = maxMemory,
|
.setMaxMemory(maxMemory)
|
||||||
minMemory = minMemory,
|
.setMinMemory(minMemory)
|
||||||
metaspace = permSize.toIntOrNull(),
|
.setMetaspace(permSize.toIntOrNull())
|
||||||
width = width,
|
.setWidth(width)
|
||||||
height = height,
|
.setHeight(height)
|
||||||
fullscreen = fullscreen,
|
.setFullscreen(fullscreen)
|
||||||
serverIp = serverIp,
|
.setServerIp(serverIp)
|
||||||
wrapper = wrapper,
|
.setWrapper(wrapper)
|
||||||
proxyHost = Settings.proxyHost,
|
.setProxyHost(Settings.proxyHost)
|
||||||
proxyPort = Settings.proxyPort,
|
.setProxyPort(Settings.proxyPort)
|
||||||
proxyUser = Settings.proxyUser,
|
.setProxyUser(Settings.proxyUser)
|
||||||
proxyPass = Settings.proxyPass,
|
.setProxyPass(Settings.proxyPass)
|
||||||
precalledCommand = precalledCommand,
|
.setPrecalledCommand(precalledCommand)
|
||||||
noGeneratedJVMArgs = noJVMArgs
|
.setNoGeneratedJVMArgs(noJVMArgs)
|
||||||
)
|
.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object Serializer: JsonSerializer<VersionSetting>, JsonDeserializer<VersionSetting> {
|
companion object Serializer: JsonSerializer<VersionSetting>, JsonDeserializer<VersionSetting> {
|
||||||
@@ -264,7 +268,7 @@ class VersionSetting() {
|
|||||||
addProperty("usesGlobal", src.usesGlobal)
|
addProperty("usesGlobal", src.usesGlobal)
|
||||||
addProperty("javaArgs", src.javaArgs)
|
addProperty("javaArgs", src.javaArgs)
|
||||||
addProperty("minecraftArgs", src.minecraftArgs)
|
addProperty("minecraftArgs", src.minecraftArgs)
|
||||||
addProperty("maxMemory", if (src.maxMemory <= 0) OS.SUGGESTED_MEMORY else src.maxMemory)
|
addProperty("maxMemory", if (src.maxMemory <= 0) OperatingSystem.SUGGESTED_MEMORY.toInt() else src.maxMemory)
|
||||||
addProperty("minMemory", src.minMemory)
|
addProperty("minMemory", src.minMemory)
|
||||||
addProperty("permSize", src.permSize)
|
addProperty("permSize", src.permSize)
|
||||||
addProperty("width", src.width)
|
addProperty("width", src.width)
|
||||||
@@ -290,8 +294,8 @@ class VersionSetting() {
|
|||||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
||||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||||
|
|
||||||
var maxMemoryN = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive, OS.SUGGESTED_MEMORY)
|
var maxMemoryN = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive, OperatingSystem.SUGGESTED_MEMORY.toInt())
|
||||||
if (maxMemoryN <= 0) maxMemoryN = OS.SUGGESTED_MEMORY
|
if (maxMemoryN <= 0) maxMemoryN = OperatingSystem.SUGGESTED_MEMORY.toInt()
|
||||||
|
|
||||||
return VersionSetting().apply {
|
return VersionSetting().apply {
|
||||||
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
|||||||
import org.jackhuang.hmcl.game.AccountHelper
|
import org.jackhuang.hmcl.game.AccountHelper
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane() {
|
class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane() {
|
||||||
@@ -83,10 +84,10 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
|||||||
btnRefresh.setOnMouseClicked {
|
btnRefresh.setOnMouseClicked {
|
||||||
pgsSkin.isVisible = true
|
pgsSkin.isVisible = true
|
||||||
AccountHelper.refreshSkinAsync(account)
|
AccountHelper.refreshSkinAsync(account)
|
||||||
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
.subscribe(Schedulers.javafx()) { loadSkin() }
|
||||||
}
|
}
|
||||||
AccountHelper.loadSkinAsync(account)
|
AccountHelper.loadSkinAsync(account)
|
||||||
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
.subscribe(Schedulers.javafx()) { loadSkin() }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account is OfflineAccount) { // Offline Account cannot be refreshed,
|
if (account is OfflineAccount) { // Offline Account cannot be refreshed,
|
||||||
|
|||||||
@@ -29,15 +29,17 @@ import javafx.scene.control.ToggleGroup
|
|||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
|
import org.jackhuang.hmcl.auth.OfflineAccountFactory
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.taskResult
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
import org.jackhuang.hmcl.util.onChange
|
import org.jackhuang.hmcl.util.onChange
|
||||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||||
|
import org.jackhuang.hmcl.util.taskResult
|
||||||
|
|
||||||
class AccountsPage() : StackPane(), DecoratorPage {
|
class AccountsPage() : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||||
@@ -126,8 +128,8 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
taskResult("create_account") {
|
taskResult("create_account") {
|
||||||
try {
|
try {
|
||||||
val account = when (type) {
|
val account = when (type) {
|
||||||
0 -> OfflineAccount.fromUsername(username)
|
0 -> OfflineAccountFactory.INSTANCE.fromUsername(username)
|
||||||
1 -> YggdrasilAccount.fromUsername(username, password)
|
1 -> YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||||
else -> throw UnsupportedOperationException()
|
else -> throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +138,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
}.subscribe(Scheduler.JAVAFX) {
|
}.subscribe(Schedulers.javafx()) {
|
||||||
val account: Any = it["create_account"]
|
val account: Any = it["create_account"]
|
||||||
if (account is Account) {
|
if (account is Account) {
|
||||||
Settings.addAccount(account)
|
Settings.addAccount(account)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import javafx.scene.layout.Region
|
|||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.util.JavaVersion
|
import org.jackhuang.hmcl.util.JavaVersion
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
|
|
||||||
object Controllers {
|
object Controllers {
|
||||||
lateinit var scene: Scene private set
|
lateinit var scene: Scene private set
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import javafx.scene.shape.Rectangle
|
|||||||
import javafx.util.Duration
|
import javafx.util.Duration
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
@@ -196,8 +197,8 @@ fun JFXMasonryPane.resetChildren(children: List<Node>) {
|
|||||||
fun openFolder(f: File) {
|
fun openFolder(f: File) {
|
||||||
f.mkdirs()
|
f.mkdirs()
|
||||||
val path = f.absolutePath
|
val path = f.absolutePath
|
||||||
when (OS.CURRENT_OS) {
|
when (OperatingSystem.CURRENT_OS) {
|
||||||
OS.OSX ->
|
OperatingSystem.OSX ->
|
||||||
try {
|
try {
|
||||||
Runtime.getRuntime().exec(arrayOf("/usr/bin/open", path));
|
Runtime.getRuntime().exec(arrayOf("/usr/bin/open", path));
|
||||||
} catch (ex: IOException) {
|
} catch (ex: IOException) {
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ package org.jackhuang.hmcl.ui
|
|||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import org.jackhuang.hmcl.download.game.VersionJSONSaveTask
|
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
import org.jackhuang.hmcl.game.minecraftVersion
|
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.ui.download.InstallWizardProvider
|
import org.jackhuang.hmcl.ui.download.InstallWizardProvider
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class InstallerController {
|
class InstallerController {
|
||||||
@@ -59,9 +59,9 @@ class InstallerController {
|
|||||||
val removeAction = { _: InstallerItem ->
|
val removeAction = { _: InstallerItem ->
|
||||||
val newList = LinkedList(version.libraries)
|
val newList = LinkedList(version.libraries)
|
||||||
newList.remove(library)
|
newList.remove(library)
|
||||||
VersionJSONSaveTask(profile.repository, version.copy(libraries = newList))
|
VersionJsonSaveTask(profile.repository, version.setLibraries(newList))
|
||||||
.with(task { profile.repository.refreshVersions() })
|
.with(task { profile.repository.refreshVersions() })
|
||||||
.with(task(Scheduler.JAVAFX) { loadVersion(this.profile, this.versionId) })
|
.with(task(Schedulers.javafx()) { loadVersion(this.profile, this.versionId) })
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
if (library.groupId.equals("net.minecraftforge", ignoreCase = true) && library.artifactId.equals("forge", ignoreCase = true)) {
|
if (library.groupId.equals("net.minecraftforge", ignoreCase = true) && library.artifactId.equals("forge", ignoreCase = true)) {
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ import javafx.scene.paint.Paint
|
|||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EventBus
|
||||||
import org.jackhuang.hmcl.game.AccountHelper
|
import org.jackhuang.hmcl.game.AccountHelper
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||||
|
import org.jackhuang.hmcl.util.channel
|
||||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||||
|
import org.jackhuang.hmcl.util.plusAssign
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||||
@@ -56,8 +58,8 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
|||||||
.startCategory(i18n("ui.label.profile"))
|
.startCategory(i18n("ui.label.profile"))
|
||||||
.add(profilePane)
|
.add(profilePane)
|
||||||
|
|
||||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
EventBus.EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||||
|
|
||||||
Controllers.decorator.addMenuButton.setOnMouseClicked {
|
Controllers.decorator.addMenuButton.setOnMouseClicked {
|
||||||
Controllers.decorator.showPage(ProfilePage(null))
|
Controllers.decorator.showPage(ProfilePage(null))
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import org.jackhuang.hmcl.util.*
|
|||||||
import org.w3c.dom.Document
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
class LogWindow : Stage() {
|
class LogWindow : Stage() {
|
||||||
val fatalProperty = SimpleIntegerProperty(0)
|
val fatalProperty = SimpleIntegerProperty(0)
|
||||||
@@ -47,6 +48,7 @@ class LogWindow : Stage() {
|
|||||||
val debugProperty = SimpleIntegerProperty(0)
|
val debugProperty = SimpleIntegerProperty(0)
|
||||||
|
|
||||||
val impl = LogWindowImpl()
|
val impl = LogWindowImpl()
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scene = Scene(impl, 800.0, 480.0)
|
scene = Scene(impl, 800.0, 480.0)
|
||||||
@@ -74,6 +76,8 @@ class LogWindow : Stage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun waitForShown() = latch.await()
|
||||||
|
|
||||||
inner class LogWindowImpl: StackPane() {
|
inner class LogWindowImpl: StackPane() {
|
||||||
@FXML lateinit var webView: WebView
|
@FXML lateinit var webView: WebView
|
||||||
@FXML lateinit var btnFatals: ToggleButton
|
@FXML lateinit var btnFatals: ToggleButton
|
||||||
@@ -97,6 +101,7 @@ class LogWindow : Stage() {
|
|||||||
document = engine.document
|
document = engine.document
|
||||||
body = document.getElementsByTagName("body").item(0)
|
body = document.getElementsByTagName("body").item(0)
|
||||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||||
|
latch.countDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,19 @@ import javafx.scene.image.Image
|
|||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EventBus
|
||||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
|
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper
|
import org.jackhuang.hmcl.game.LauncherHelper
|
||||||
import org.jackhuang.hmcl.game.minecraftVersion
|
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.channel
|
||||||
import org.jackhuang.hmcl.util.onChange
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
import org.jackhuang.hmcl.util.plusAssign
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see /assets/fxml/main.fxml
|
* @see /assets/fxml/main.fxml
|
||||||
@@ -59,9 +61,9 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
btnLaunch.limitWidth(40.0)
|
btnLaunch.limitWidth(40.0)
|
||||||
btnLaunch.limitHeight(40.0)
|
btnLaunch.limitHeight(40.0)
|
||||||
|
|
||||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += { -> loadVersions() }
|
EventBus.EVENT_BUS.channel<RefreshedVersionsEvent>() += { -> loadVersions() }
|
||||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
EventBus.EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||||
|
|
||||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
||||||
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import javafx.stage.FileChooser
|
|||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.task
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.util.onChange
|
import org.jackhuang.hmcl.util.onChange
|
||||||
import org.jackhuang.hmcl.util.onChangeAndOperateWeakly
|
import org.jackhuang.hmcl.util.onChangeAndOperateWeakly
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ModController {
|
class ModController {
|
||||||
@@ -77,7 +78,7 @@ class ModController {
|
|||||||
modManager.removeMods(versionId, modInfo)
|
modManager.removeMods(versionId, modInfo)
|
||||||
loadMods(modManager, versionId)
|
loadMods(modManager, versionId)
|
||||||
}.apply {
|
}.apply {
|
||||||
modInfo.activeProperty.onChange {
|
modInfo.activeProperty().onChange {
|
||||||
if (it)
|
if (it)
|
||||||
styleClass -= "disabled"
|
styleClass -= "disabled"
|
||||||
else
|
else
|
||||||
@@ -91,7 +92,7 @@ class ModController {
|
|||||||
runOnUiThread { rootPane.children += contentPane }
|
runOnUiThread { rootPane.children += contentPane }
|
||||||
it["list"] = list
|
it["list"] = list
|
||||||
}
|
}
|
||||||
}.subscribe(Scheduler.JAVAFX) { variables ->
|
}.subscribe(Schedulers.javafx()) { variables ->
|
||||||
parentTab.selectionModel.selectedItemProperty().onChangeAndOperateWeakly {
|
parentTab.selectionModel.selectedItemProperty().onChangeAndOperateWeakly {
|
||||||
if (it?.userData == this) {
|
if (it?.userData == this) {
|
||||||
modPane.children.setAll(variables.get<List<ModItem>>("list"))
|
modPane.children.setAll(variables.get<List<ModItem>>("list"))
|
||||||
@@ -106,6 +107,6 @@ class ModController {
|
|||||||
chooser.extensionFilters.setAll(FileChooser.ExtensionFilter("Mod", "*.jar", "*.zip", "*.litemod"))
|
chooser.extensionFilters.setAll(FileChooser.ExtensionFilter("Mod", "*.jar", "*.zip", "*.litemod"))
|
||||||
val res = chooser.showOpenDialog(Controllers.stage) ?: return
|
val res = chooser.showOpenDialog(Controllers.stage) ?: return
|
||||||
task { modManager.addMod(versionId, res) }
|
task { modManager.addMod(versionId, res) }
|
||||||
.subscribe(task(Scheduler.JAVAFX) { loadMods(modManager, versionId) })
|
.subscribe(task(Schedulers.javafx()) { loadMods(modManager, versionId) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,10 +53,10 @@ class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : Bo
|
|||||||
style = "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
style = "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
||||||
JFXDepthManager.setDepth(this, 1)
|
JFXDepthManager.setDepth(this, 1)
|
||||||
lblModFileName.text = info.fileName
|
lblModFileName.text = info.fileName
|
||||||
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.mcversion}, Authors: ${info.authors}"
|
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.gameVersion}, Authors: ${info.authors}"
|
||||||
chkEnabled.isSelected = info.isActive
|
chkEnabled.isSelected = info.isActive
|
||||||
chkEnabled.selectedProperty().onChange {
|
chkEnabled.selectedProperty().onChange {
|
||||||
info.activeProperty.set(it)
|
info.activeProperty().set(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,25 +24,21 @@ import javafx.scene.Node
|
|||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.control.Toggle
|
import javafx.scene.control.Toggle
|
||||||
import javafx.scene.control.ToggleGroup
|
|
||||||
import javafx.scene.image.Image
|
import javafx.scene.image.Image
|
||||||
import javafx.scene.image.ImageView
|
import javafx.scene.image.ImageView
|
||||||
import javafx.scene.layout.BorderPane
|
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import javafx.stage.DirectoryChooser
|
|
||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.ui.construct.ComponentList
|
import org.jackhuang.hmcl.ui.construct.ComponentList
|
||||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem
|
import org.jackhuang.hmcl.ui.construct.MultiFileItem
|
||||||
import org.jackhuang.hmcl.ui.construct.NumberValidator
|
import org.jackhuang.hmcl.ui.construct.NumberValidator
|
||||||
import org.jackhuang.hmcl.util.JavaVersion
|
import org.jackhuang.hmcl.util.JavaVersion
|
||||||
import org.jackhuang.hmcl.util.OS
|
import org.jackhuang.hmcl.util.OperatingSystem
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
|
|
||||||
class VersionSettingsController {
|
class VersionSettingsController {
|
||||||
var lastVersionSetting: VersionSetting? = null
|
var lastVersionSetting: VersionSetting? = null
|
||||||
@@ -74,7 +70,7 @@ class VersionSettingsController {
|
|||||||
lateinit var versionId: String
|
lateinit var versionId: String
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OS.TOTAL_MEMORY}MB"
|
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OperatingSystem.TOTAL_MEMORY}MB"
|
||||||
|
|
||||||
scroll.smoothScrolling()
|
scroll.smoothScrolling()
|
||||||
|
|
||||||
@@ -102,9 +98,9 @@ class VersionSettingsController {
|
|||||||
|
|
||||||
task {
|
task {
|
||||||
it["list"] = JavaVersion.getJREs().values.map { javaVersion ->
|
it["list"] = JavaVersion.getJREs().values.map { javaVersion ->
|
||||||
javaItem.createChildren(javaVersion.longVersion, javaVersion.binary.absolutePath, javaVersion)
|
javaItem.createChildren(javaVersion.version, javaVersion.binary.absolutePath, javaVersion)
|
||||||
}
|
}
|
||||||
}.subscribe(Scheduler.JAVAFX) {
|
}.subscribe(Schedulers.javafx()) {
|
||||||
javaItem.loadChildren(it.get<Collection<Node>>("list"))
|
javaItem.loadChildren(it.get<Collection<Node>>("list"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +171,7 @@ class VersionSettingsController {
|
|||||||
if (newValue == javaItem.radioCustom) { // Custom
|
if (newValue == javaItem.radioCustom) { // Custom
|
||||||
version.java = "Custom"
|
version.java = "Custom"
|
||||||
} else {
|
} else {
|
||||||
version.java = ((newValue as JFXRadioButton).userData as JavaVersion).longVersion
|
version.java = ((newValue as JFXRadioButton).userData as JavaVersion).version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
javaItem.group.properties[javaGroupKey] = listener
|
javaItem.group.properties[javaGroupKey] = listener
|
||||||
@@ -220,7 +216,7 @@ class VersionSettingsController {
|
|||||||
|
|
||||||
private fun initJavaSubtitle(version: VersionSetting) {
|
private fun initJavaSubtitle(version: VersionSetting) {
|
||||||
task { it["java"] = version.javaVersion }
|
task { it["java"] = version.javaVersion }
|
||||||
.subscribe(task(Scheduler.JAVAFX) { javaItem.subtitle = it.get<JavaVersion?>("java")?.binary?.absolutePath ?: "Invalid Java Directory" })
|
.subscribe(task(Schedulers.javafx()) { javaItem.subtitle = it.get<JavaVersion?>("java")?.binary?.absolutePath ?: "Invalid Java Directory" })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initGameDirSubtitle(version: VersionSetting) {
|
private fun initGameDirSubtitle(version: VersionSetting) {
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ import javafx.scene.layout.StackPane
|
|||||||
import org.jackhuang.hmcl.auth.AuthInfo
|
import org.jackhuang.hmcl.auth.AuthInfo
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.taskResult
|
import org.jackhuang.hmcl.util.taskResult
|
||||||
|
|
||||||
class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, private val success: (AuthInfo) -> Unit, private val failed: () -> Unit) : StackPane() {
|
class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, private val success: (AuthInfo) -> Unit, private val failed: () -> Unit) : StackPane() {
|
||||||
@FXML lateinit var lblUsername: Label
|
@FXML lateinit var lblUsername: Label
|
||||||
@@ -54,12 +55,12 @@ class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, privat
|
|||||||
lblCreationWarning.text = ""
|
lblCreationWarning.text = ""
|
||||||
taskResult("login") {
|
taskResult("login") {
|
||||||
try {
|
try {
|
||||||
val account = YggdrasilAccount.fromUsername(username, password)
|
val account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||||
account.logIn(Settings.proxy)
|
account.logIn(Settings.proxy)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
}.subscribe(Scheduler.JAVAFX) {
|
}.subscribe(Schedulers.javafx()) {
|
||||||
val account: Any = it["login"]
|
val account: Any = it["login"]
|
||||||
if (account is AuthInfo) {
|
if (account is AuthInfo) {
|
||||||
success(account)
|
success(account)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.ui.download
|
package org.jackhuang.hmcl.ui.download
|
||||||
|
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask
|
import org.jackhuang.hmcl.game.HMCLModpackInstallTask
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackManifest
|
import org.jackhuang.hmcl.game.HMCLModpackManifest
|
||||||
import org.jackhuang.hmcl.game.MMCInstallVersionSettingTask
|
import org.jackhuang.hmcl.game.MMCInstallVersionSettingTask
|
||||||
@@ -27,9 +26,9 @@ import org.jackhuang.hmcl.setting.EnumGameDirectory
|
|||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class DownloadWizardProvider(): WizardProvider() {
|
class DownloadWizardProvider(): WizardProvider() {
|
||||||
@@ -79,11 +78,11 @@ class DownloadWizardProvider(): WizardProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return when (modpack.manifest) {
|
return when (modpack.manifest) {
|
||||||
is CurseForgeModpackManifest -> CurseForgeModpackInstallTask(profile.dependency, selectedFile, modpack.manifest as CurseForgeModpackManifest, name)
|
is CurseManifest -> CurseInstallTask(profile.dependency, selectedFile, modpack.manifest as CurseManifest, name)
|
||||||
is HMCLModpackManifest -> HMCLModpackInstallTask(profile, selectedFile, modpack, name)
|
is HMCLModpackManifest -> HMCLModpackInstallTask(profile, selectedFile, modpack, name)
|
||||||
is InstanceConfiguration -> MMCModpackInstallTask(profile.dependency, selectedFile, modpack.manifest as InstanceConfiguration, name) with MMCInstallVersionSettingTask(profile, modpack.manifest as InstanceConfiguration, name)
|
is MultiMCInstanceConfiguration -> MultiMCModpackInstallTask(profile.dependency, selectedFile, modpack.manifest as MultiMCInstanceConfiguration, name).with(MMCInstallVersionSettingTask(profile, modpack.manifest as MultiMCInstanceConfiguration, name))
|
||||||
else -> throw Error()
|
else -> throw Error()
|
||||||
} with finalizeTask
|
}.with(finalizeTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finish(settings: MutableMap<String, Any>): Any? {
|
override fun finish(settings: MutableMap<String, Any>): Any? {
|
||||||
|
|||||||
@@ -2,20 +2,12 @@ package org.jackhuang.hmcl.ui.download
|
|||||||
|
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask
|
|
||||||
import org.jackhuang.hmcl.game.HMCLModpackManifest
|
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.Version
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackInstallTask
|
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
|
||||||
import org.jackhuang.hmcl.mod.Modpack
|
import org.jackhuang.hmcl.mod.Modpack
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
||||||
|
import org.jackhuang.hmcl.util.task
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class InstallWizardProvider(val profile: Profile, val gameVersion: String, val version: Version, val forge: String? = null, val liteloader: String? = null, val optifine: String? = null): WizardProvider() {
|
class InstallWizardProvider(val profile: Profile, val gameVersion: String, val version: Version, val forge: String? = null, val liteloader: String? = null, val optifine: String? = null): WizardProvider() {
|
||||||
@@ -27,20 +19,20 @@ class InstallWizardProvider(val profile: Profile, val gameVersion: String, val v
|
|||||||
var ret = task {}
|
var ret = task {}
|
||||||
|
|
||||||
if (settings.containsKey("forge"))
|
if (settings.containsKey("forge"))
|
||||||
ret = ret with profile.dependency.installLibraryAsync(gameVersion, version, "forge", settings["forge"] as String)
|
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "forge", settings["forge"] as String))
|
||||||
|
|
||||||
if (settings.containsKey("liteloader"))
|
if (settings.containsKey("liteloader"))
|
||||||
ret = ret with profile.dependency.installLibraryAsync(gameVersion, version, "liteloader", settings["liteloader"] as String)
|
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "liteloader", settings["liteloader"] as String))
|
||||||
|
|
||||||
if (settings.containsKey("optifine"))
|
if (settings.containsKey("optifine"))
|
||||||
ret = ret with profile.dependency.installLibraryAsync(gameVersion, version, "optifine", settings["optifine"] as String)
|
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "optifine", settings["optifine"] as String))
|
||||||
|
|
||||||
return ret with task { profile.repository.refreshVersions() }
|
return ret.with(task { profile.repository.refreshVersions() })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createPage(controller: WizardController, step: Int, settings: MutableMap<String, Any>): Node {
|
override fun createPage(controller: WizardController, step: Int, settings: MutableMap<String, Any>): Node {
|
||||||
return when (step) {
|
return when (step) {
|
||||||
0 -> AdditionalInstallersPage(this, controller, profile.repository, BMCLAPIDownloadProvider)
|
0 -> AdditionalInstallersPage(this, controller, profile.repository, BMCLAPIDownloadProvider.INSTANCE)
|
||||||
else -> throw IllegalStateException()
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import javafx.scene.layout.StackPane
|
|||||||
import org.jackhuang.hmcl.download.DownloadProvider
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion
|
import org.jackhuang.hmcl.download.RemoteVersion
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor
|
import org.jackhuang.hmcl.task.TaskExecutor
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
||||||
@@ -53,9 +54,9 @@ class VersionsPage(private val controller: WizardController, private val gameVer
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun refresh() {
|
override fun refresh() {
|
||||||
executor = versionList.refreshAsync(downloadProvider).subscribe(Scheduler.JAVAFX) {
|
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx()) {
|
||||||
val versions = ArrayList(versionList.getVersions(gameVersion))
|
val versions = ArrayList(versionList.getVersions(gameVersion))
|
||||||
versions.sortWith(RemoteVersion)
|
versions.sortWith(RemoteVersion.RemoteVersionComparator.INSTANCE)
|
||||||
for (version in versions) {
|
for (version in versions) {
|
||||||
list.items.add(VersionsPageItem(version))
|
list.items.add(VersionsPageItem(version))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ class ExportWizardProvider(private val profile: Profile, private val version: St
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return HMCLModpackExportTask(profile.repository, version, settings[ModpackFileSelectionPage.MODPACK_FILE_SELECTION] as List<String>,
|
return HMCLModpackExportTask(profile.repository, version, settings[ModpackFileSelectionPage.MODPACK_FILE_SELECTION] as List<String>,
|
||||||
Modpack(
|
Modpack(
|
||||||
name = settings[ModpackInfoPage.MODPACK_NAME] as String,
|
settings[ModpackInfoPage.MODPACK_NAME] as String,
|
||||||
author = settings[ModpackInfoPage.MODPACK_AUTHOR] as String,
|
settings[ModpackInfoPage.MODPACK_AUTHOR] as String,
|
||||||
version = settings[ModpackInfoPage.MODPACK_VERSION] as String,
|
settings[ModpackInfoPage.MODPACK_VERSION] as String,
|
||||||
description = settings[ModpackInfoPage.MODPACK_DESCRIPTION] as String
|
null,
|
||||||
|
settings[ModpackInfoPage.MODPACK_DESCRIPTION] as String,
|
||||||
|
null
|
||||||
), settings[ModpackInfoPage.MODPACK_FILE] as File)
|
), settings[ModpackInfoPage.MODPACK_FILE] as File)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ import javafx.application.Platform
|
|||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.*
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor
|
|
||||||
import org.jackhuang.hmcl.task.TaskListener
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
@@ -94,28 +91,28 @@ interface AbstractWizardDisplayer : WizardDisplayer {
|
|||||||
|
|
||||||
navigateTo(StackPane().apply { children += vbox }, Navigation.NavigationDirection.FINISH)
|
navigateTo(StackPane().apply { children += vbox }, Navigation.NavigationDirection.FINISH)
|
||||||
|
|
||||||
task.with(org.jackhuang.hmcl.task.task(Scheduler.JAVAFX) {
|
task.with(org.jackhuang.hmcl.util.task(Schedulers.javafx()) {
|
||||||
navigateTo(Label("Successful"), Navigation.NavigationDirection.FINISH)
|
navigateTo(Label("Successful"), Navigation.NavigationDirection.FINISH)
|
||||||
}).executor().apply {
|
}).executor().apply {
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
taskListener = object : TaskListener {
|
taskListener = object : TaskListener() {
|
||||||
override fun onReady(task: Task) {
|
override fun onReady(task: Task) {
|
||||||
Platform.runLater { tasksBar.progressProperty().set(finishedTasks * 1.0 / totTask.get()) }
|
Platform.runLater { tasksBar.progressProperty().set(finishedTasks * 1.0 / runningTasks) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinished(task: Task) {
|
override fun onFinished(task: Task) {
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
label.text = task.title
|
label.text = task.name
|
||||||
++finishedTasks
|
++finishedTasks
|
||||||
tasksBar.progressProperty().set(finishedTasks * 1.0 / totTask.get())
|
tasksBar.progressProperty().set(finishedTasks * 1.0 / runningTasks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailed(task: Task, throwable: Throwable) {
|
override fun onFailed(task: Task, throwable: Throwable) {
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
label.text = task.title
|
label.text = task.name
|
||||||
++finishedTasks
|
++finishedTasks
|
||||||
tasksBar.progressProperty().set(finishedTasks * 1.0 / totTask.get())
|
tasksBar.progressProperty().set(finishedTasks * 1.0 / runningTasks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ import java.util.jar.Pack200
|
|||||||
import java.util.jar.JarOutputStream
|
import java.util.jar.JarOutputStream
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import org.jackhuang.hmcl.util.OS
|
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.ui.alert
|
import org.jackhuang.hmcl.ui.alert
|
||||||
|
import org.jackhuang.hmcl.util.Constants.GSON
|
||||||
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import org.jackhuang.hmcl.util.VersionNumber
|
import org.jackhuang.hmcl.util.VersionNumber
|
||||||
|
import java.net.Proxy
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
class AppDataUpgrader : IUpgrader {
|
class AppDataUpgrader : IUpgrader {
|
||||||
@@ -123,18 +125,18 @@ class AppDataUpgrader : IUpgrader {
|
|||||||
else {
|
else {
|
||||||
var url = URL_PUBLISH
|
var url = URL_PUBLISH
|
||||||
if (map != null)
|
if (map != null)
|
||||||
if (map.containsKey(OS.CURRENT_OS.checkedName))
|
if (map.containsKey(OperatingSystem.CURRENT_OS.checkedName))
|
||||||
url = map.get(OS.CURRENT_OS.checkedName)!!
|
url = map.get(OperatingSystem.CURRENT_OS.checkedName)!!
|
||||||
else if (map.containsKey(OS.UNKNOWN.checkedName))
|
else if (map.containsKey(OperatingSystem.UNKNOWN.checkedName))
|
||||||
url = map.get(OS.UNKNOWN.checkedName)!!
|
url = map.get(OperatingSystem.UNKNOWN.checkedName)!!
|
||||||
try {
|
try {
|
||||||
java.awt.Desktop.getDesktop().browse(URI(url))
|
java.awt.Desktop.getDesktop().browse(URI(url))
|
||||||
} catch (e: URISyntaxException) {
|
} catch (e: URISyntaxException) {
|
||||||
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
||||||
OS.setClipboard(url)
|
OperatingSystem.setClipboard(url)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
||||||
OS.setClipboard(url)
|
OperatingSystem.setClipboard(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -144,16 +146,17 @@ class AppDataUpgrader : IUpgrader {
|
|||||||
|
|
||||||
class AppDataUpgraderPackGzTask(downloadLink: String, private val newestVersion: String, private val expectedHash: String) : Task() {
|
class AppDataUpgraderPackGzTask(downloadLink: String, private val newestVersion: String, private val expectedHash: String) : Task() {
|
||||||
private val tempFile: File = File.createTempFile("hmcl", ".pack.gz")
|
private val tempFile: File = File.createTempFile("hmcl", ".pack.gz")
|
||||||
override val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, expectedHash))
|
private val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, Proxy.NO_PROXY, expectedHash))
|
||||||
|
override fun getDependents() = dependents
|
||||||
|
|
||||||
init {
|
init {
|
||||||
onDone += { event -> if (event.failed) tempFile.delete() }
|
onDone() += { event -> if (event.isFailed) tempFile.delete() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val json = HashMap<String, String>()
|
val json = HashMap<String, String>()
|
||||||
var f = getSelf(newestVersion)
|
var f = getSelf(newestVersion)
|
||||||
if (!f.parentFile.makeDirectory())
|
if (!FileUtils.makeDirectory(f.parentFile))
|
||||||
throw IOException("Failed to make directories: " + f.parent)
|
throw IOException("Failed to make directories: " + f.parent)
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
@@ -187,15 +190,15 @@ class AppDataUpgrader : IUpgrader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AppDataUpgraderJarTask(downloadLink: String, private val newestVersion: String, expectedHash: String) : Task() {
|
class AppDataUpgraderJarTask(downloadLink: String, private val newestVersion: String, expectedHash: String) : Task() {
|
||||||
override var title = "Upgrade"
|
|
||||||
set(value) {}
|
|
||||||
private val tempFile = File.createTempFile("hmcl", ".jar")
|
private val tempFile = File.createTempFile("hmcl", ".jar")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
onDone += { event -> if (event.failed) tempFile.delete() }
|
name = "Upgrade"
|
||||||
|
onDone() += { event -> if (event.isFailed) tempFile.delete() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, expectedHash))
|
private val dependents = listOf(FileDownloadTask(downloadLink.toURL(), tempFile, Proxy.NO_PROXY, expectedHash))
|
||||||
|
override fun getDependents() = dependents
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val json = HashMap<String, String>()
|
val json = HashMap<String, String>()
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import java.io.IOException
|
|||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import org.jackhuang.hmcl.task.TaskResult
|
import org.jackhuang.hmcl.task.TaskResult
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.Constants.GSON
|
||||||
|
import org.jackhuang.hmcl.util.Logging.LOG
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
|
||||||
@@ -58,10 +60,10 @@ class UpdateChecker(var base: VersionNumber, var type: String) {
|
|||||||
*/
|
*/
|
||||||
fun process(showMessage: Boolean): TaskResult<VersionNumber> {
|
fun process(showMessage: Boolean): TaskResult<VersionNumber> {
|
||||||
return object : TaskResult<VersionNumber>() {
|
return object : TaskResult<VersionNumber>() {
|
||||||
override val id = "update_checker.process"
|
override fun getId() = "update_checker.process"
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
if (newVersion == null) {
|
if (newVersion == null) {
|
||||||
versionString = ("http://huangyuhui.duapp.com/info.php?type=$type").toURL().doGet()
|
versionString = NetworkUtils.doGet("http://huangyuhui.duapp.com/info.php?type=$type".toURL())
|
||||||
newVersion = VersionNumber.asVersion(versionString!!)
|
newVersion = VersionNumber.asVersion(versionString!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,12 +85,12 @@ class UpdateChecker(var base: VersionNumber, var type: String) {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
fun requestDownloadLink(): TaskResult<Map<String, String>> {
|
fun requestDownloadLink(): TaskResult<Map<String, String>> {
|
||||||
return object : TaskResult<Map<String, String>>() {
|
return object : TaskResult<Map<String, String>>() {
|
||||||
override val id = "update_checker.request_download_link"
|
override fun getId() = "update_checker.request_download_link"
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (download_link == null)
|
if (download_link == null)
|
||||||
try {
|
try {
|
||||||
download_link = GSON.fromJson(("http://huangyuhui.duapp.com/update_link.php?type=$type").toURL().doGet(), Map::class.java) as Map<String, String>
|
download_link = GSON.fromJson(NetworkUtils.doGet("http://huangyuhui.duapp.com/update_link.php?type=$type".toURL()), Map::class.java) as Map<String, String>
|
||||||
} catch (e: JsonSyntaxException) {
|
} catch (e: JsonSyntaxException) {
|
||||||
LOG.log(Level.WARNING, "Failed to get update link.", e)
|
LOG.log(Level.WARNING, "Failed to get update link.", e)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|||||||
@@ -17,10 +17,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonParseException
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
import javafx.beans.property.Property
|
import javafx.beans.property.Property
|
||||||
|
import javafx.event.Event.fireEvent
|
||||||
|
import org.jackhuang.hmcl.event.EventBus
|
||||||
|
import org.jackhuang.hmcl.event.EventManager
|
||||||
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers
|
||||||
|
import org.jackhuang.hmcl.task.Task
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult
|
||||||
|
import org.jackhuang.hmcl.util.Constants.UI_THREAD_SCHEDULER
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.net.URL
|
||||||
|
import java.rmi.activation.Activatable.unregister
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.Callable
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
inline fun ignoreException(func: () -> Unit) {
|
inline fun ignoreException(func: () -> Unit) {
|
||||||
try {
|
try {
|
||||||
@@ -34,28 +49,6 @@ inline fun ignoreThrowable(func: () -> Unit) {
|
|||||||
} catch (ignore: Throwable) {}
|
} catch (ignore: Throwable) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <K, V> unmodifiableMap(map: Map<K, V>?): Map<K, V>? =
|
|
||||||
if (map == null) null
|
|
||||||
else Collections.unmodifiableMap(map)
|
|
||||||
|
|
||||||
fun <K, V> copyMap(map: Map<K, V>?): MutableMap<K, V>? =
|
|
||||||
if (map == null) null
|
|
||||||
else HashMap(map)
|
|
||||||
|
|
||||||
fun <T> unmodifiableList(list: List<T>?): List<T>? =
|
|
||||||
if (list == null) null
|
|
||||||
else Collections.unmodifiableList(list)
|
|
||||||
|
|
||||||
fun <T> copyList(list: List<T>?): MutableList<T>? =
|
|
||||||
if (list == null) null
|
|
||||||
else LinkedList(list)
|
|
||||||
|
|
||||||
fun <T> merge(vararg c: Collection<T>?): List<T> = LinkedList<T>().apply {
|
|
||||||
for (a in c)
|
|
||||||
if (a != null)
|
|
||||||
addAll(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isBlank(str: String?) = str?.isBlank() ?: true
|
fun isBlank(str: String?) = str?.isBlank() ?: true
|
||||||
fun isNotBlank(str: String?) = !isBlank(str)
|
fun isNotBlank(str: String?) = !isBlank(str)
|
||||||
|
|
||||||
@@ -84,42 +77,7 @@ fun String.asVersion(): String? {
|
|||||||
|
|
||||||
fun Any?.toStringOrEmpty() = this?.toString().orEmpty()
|
fun Any?.toStringOrEmpty() = this?.toString().orEmpty()
|
||||||
|
|
||||||
fun parseParams(addBefore: String, objects: Collection<*>, addAfter: String): String {
|
fun String.toURL() = URL(this)
|
||||||
return parseParams(addBefore, objects.toTypedArray(), addAfter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseParams(addBefore: String, objects: Array<*>, addAfter: String): String {
|
|
||||||
return parseParams({ addBefore }, objects, { addAfter })
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseParams(beforeFunc: (Any?) -> String, params: Array<*>?, afterFunc: (Any?) -> String): String {
|
|
||||||
if (params == null)
|
|
||||||
return ""
|
|
||||||
val sb = StringBuilder()
|
|
||||||
for (i in params.indices) {
|
|
||||||
val param = params[i]
|
|
||||||
val addBefore = beforeFunc(param)
|
|
||||||
val addAfter = afterFunc(param)
|
|
||||||
if (i > 0)
|
|
||||||
sb.append(addAfter).append(addBefore)
|
|
||||||
if (param == null)
|
|
||||||
sb.append("null")
|
|
||||||
else if (param.javaClass.isArray) {
|
|
||||||
sb.append("[")
|
|
||||||
if (param is Array<*>) {
|
|
||||||
sb.append(parseParams(beforeFunc, param, afterFunc))
|
|
||||||
} else
|
|
||||||
for (j in 0..java.lang.reflect.Array.getLength(param) - 1) {
|
|
||||||
if (j > 0)
|
|
||||||
sb.append(addAfter)
|
|
||||||
sb.append(addBefore).append(java.lang.reflect.Array.get(param, j))
|
|
||||||
}
|
|
||||||
sb.append("]")
|
|
||||||
} else
|
|
||||||
sb.append(addBefore).append(params[i])
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Collection<String>.containsOne(vararg matcher: String): Boolean {
|
fun Collection<String>.containsOne(vararg matcher: String): Boolean {
|
||||||
for (a in this)
|
for (a in this)
|
||||||
@@ -131,9 +89,34 @@ fun Collection<String>.containsOne(vararg matcher: String): Boolean {
|
|||||||
|
|
||||||
fun <T> Property<in T>.updateAsync(newValue: T, update: AtomicReference<T>) {
|
fun <T> Property<in T>.updateAsync(newValue: T, update: AtomicReference<T>) {
|
||||||
if (update.getAndSet(newValue) == null) {
|
if (update.getAndSet(newValue) == null) {
|
||||||
UI_THREAD_SCHEDULER {
|
UI_THREAD_SCHEDULER.accept(Runnable {
|
||||||
val current = update.getAndSet(null)
|
val current = update.getAndSet(null)
|
||||||
this.value = current
|
this.value = current
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> typeOf(): Type = object : TypeToken<T>() {}.type
|
||||||
|
|
||||||
|
inline fun <reified T> Gson.fromJson(json: String): T? = fromJson<T>(json, T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T> Gson.fromJsonQuietly(json: String): T? {
|
||||||
|
try {
|
||||||
|
return fromJson<T>(json)
|
||||||
|
} catch (json: JsonParseException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun task(scheduler: Scheduler = Schedulers.defaultScheduler(), closure: (AutoTypingMap<String>) -> Unit): Task = Task.of(closure, scheduler)
|
||||||
|
fun <V> taskResult(id: String, callable: Callable<V>): TaskResult<V> = Task.ofResult(id, callable)
|
||||||
|
fun <V> taskResult(id: String, callable: (AutoTypingMap<String>) -> V): TaskResult<V> = Task.ofResult(id, callable)
|
||||||
|
|
||||||
|
fun InputStream.readFullyAsString() = IOUtils.readFullyAsString(this)
|
||||||
|
inline fun <reified T : EventObject> EventBus.channel() = channel(T::class.java)
|
||||||
|
|
||||||
|
operator fun <T : EventObject> EventManager<T>.plusAssign(func: (T) -> Unit) = register(func)
|
||||||
|
operator fun <T : EventObject> EventManager<T>.plusAssign(func: () -> Unit) = register(func)
|
||||||
|
operator fun <T : EventObject> EventManager<T>.minusAssign(func: (T) -> Unit) = unregister(func)
|
||||||
|
operator fun <T : EventObject> EventManager<T>.minusAssign(func: () -> Unit) = unregister(func)
|
||||||
|
operator fun <T : EventObject> EventManager<T>.invoke(event: T) = fireEvent(event)
|
||||||
40
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java
Normal file
40
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/Account.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public abstract class Account {
|
||||||
|
|
||||||
|
public abstract String getUsername();
|
||||||
|
|
||||||
|
public AuthInfo logIn() throws AuthenticationException {
|
||||||
|
return logIn(Proxy.NO_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract AuthInfo logIn(Proxy proxy) throws AuthenticationException;
|
||||||
|
|
||||||
|
public abstract void logOut();
|
||||||
|
|
||||||
|
public abstract Map<Object, Object> toStorage();
|
||||||
|
}
|
||||||
@@ -15,17 +15,21 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tasks that provides a way to execute tasks parallelly.
|
|
||||||
* Fails when some of [tasks] failed.
|
|
||||||
*
|
*
|
||||||
* @param tasks the tasks that can be executed parallelly.
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
class ParallelTask(vararg tasks: Task): Task() {
|
public abstract class AccountFactory<T extends Account> {
|
||||||
override val hidden: Boolean = true
|
|
||||||
override val dependents: Collection<Task> = listOf(*tasks)
|
|
||||||
|
|
||||||
override fun execute() {}
|
public final T fromUsername(String username) {
|
||||||
}
|
return fromUsername(username, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract T fromUsername(String username, String password);
|
||||||
|
|
||||||
|
public abstract T fromStorage(Map<Object, Object> storage);
|
||||||
|
}
|
||||||
96
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java
Normal file
96
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/AuthInfo.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class AuthInfo {
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private final String userId;
|
||||||
|
private final String authToken;
|
||||||
|
private final UserType userType;
|
||||||
|
private final String userProperties;
|
||||||
|
private final String userPropertyMap;
|
||||||
|
|
||||||
|
public AuthInfo(String username, String userId, String authToken) {
|
||||||
|
this(username, userId, authToken, UserType.LEGACY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthInfo(String username, String userId, String authToken, UserType userType) {
|
||||||
|
this(username, userId, authToken, userType, "{}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties) {
|
||||||
|
this(username, userId, authToken, userType, userProperties, "{}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthInfo(String username, String userId, String authToken, UserType userType, String userProperties, String userPropertyMap) {
|
||||||
|
this.username = username;
|
||||||
|
this.userId = userId;
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.userType = userType;
|
||||||
|
this.userProperties = userProperties;
|
||||||
|
this.userPropertyMap = userPropertyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthInfo(GameProfile profile, String authToken, UserType userType, String userProperties) {
|
||||||
|
this(profile.getName(), UUIDTypeAdapter.fromUUID(profile.getId()), authToken, userType, userProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthToken() {
|
||||||
|
return authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserType getUserType() {
|
||||||
|
return userType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties of this user.
|
||||||
|
* Don't know the difference between user properties and user property map.
|
||||||
|
*
|
||||||
|
* @return the user property map in JSON.
|
||||||
|
*/
|
||||||
|
public String getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties of this user.
|
||||||
|
* Don't know the difference between user properties and user property map.
|
||||||
|
*
|
||||||
|
* @return the user property map in JSON.
|
||||||
|
*/
|
||||||
|
public String getUserPropertyMap() {
|
||||||
|
return userPropertyMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class AuthenticationException extends Exception {
|
||||||
|
|
||||||
|
public AuthenticationException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public class OfflineAccount extends Account {
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private final String uuid;
|
||||||
|
|
||||||
|
OfflineAccount(String username, String uuid) {
|
||||||
|
Objects.requireNonNull(username);
|
||||||
|
Objects.requireNonNull(uuid);
|
||||||
|
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(username))
|
||||||
|
throw new IllegalArgumentException("Username cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
|
||||||
|
if (StringUtils.isBlank(username) || StringUtils.isBlank(uuid))
|
||||||
|
throw new AuthenticationException("Username cannot be empty");
|
||||||
|
|
||||||
|
return new AuthInfo(username, uuid, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logOut() {
|
||||||
|
// Offline account need not log out.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Object, Object> toStorage() {
|
||||||
|
return Lang.mapOf(
|
||||||
|
new Pair<>("uuid", uuid),
|
||||||
|
new Pair<>("username", username)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OfflineAccount[username=" + username + ", uuid=" + uuid + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jackhuang.hmcl.util.DigestUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
|
||||||
|
public static final OfflineAccountFactory INSTANCE = new OfflineAccountFactory();
|
||||||
|
|
||||||
|
private OfflineAccountFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineAccount fromUsername(String username, String password) {
|
||||||
|
return new OfflineAccount(username, getUUIDFromUserName(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OfflineAccount fromStorage(Map<Object, Object> storage) {
|
||||||
|
Object username = storage.get("username");
|
||||||
|
if (username == null || !(username instanceof String))
|
||||||
|
throw new IllegalStateException("Offline account configuration malformed.");
|
||||||
|
|
||||||
|
Object uuid = storage.get("uuid");
|
||||||
|
if (uuid == null || !(uuid instanceof String))
|
||||||
|
uuid = getUUIDFromUserName((String) username);
|
||||||
|
|
||||||
|
return new OfflineAccount((String) username, (String) uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getUUIDFromUserName(String username) {
|
||||||
|
return DigestUtils.md5Hex(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/UserType.java
Normal file
49
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/UserType.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public enum UserType {
|
||||||
|
LEGACY,
|
||||||
|
MOJANG;
|
||||||
|
|
||||||
|
public static UserType fromName(String name) {
|
||||||
|
return BY_NAME.get(name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserType fromLegacy(boolean isLegacy) {
|
||||||
|
return isLegacy ? LEGACY : MOJANG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
HashMap<String, UserType> byName = new HashMap<>();
|
||||||
|
for (UserType type : values())
|
||||||
|
byName.put(type.name().toLowerCase(), type);
|
||||||
|
BY_NAME = Collections.unmodifiableMap(byName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Map<String, UserType> BY_NAME;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class AuthenticationRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user name of Minecraft account.
|
||||||
|
*/
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password of Minecraft account.
|
||||||
|
*/
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The client token of this game.
|
||||||
|
*/
|
||||||
|
private final String clientToken;
|
||||||
|
|
||||||
|
private final Map<String, Object> agent = Lang.mapOf(
|
||||||
|
new Pair("name", "minecraft"),
|
||||||
|
new Pair("version", 1));
|
||||||
|
|
||||||
|
private final boolean requestUser = true;
|
||||||
|
|
||||||
|
public AuthenticationRequest(String username, String password, String clientToken) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.clientToken = clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAgent() {
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestUser() {
|
||||||
|
return requestUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public final class GameProfile {
|
||||||
|
|
||||||
|
private final UUID id;
|
||||||
|
private final String name;
|
||||||
|
private final PropertyMap properties;
|
||||||
|
private final boolean legacy;
|
||||||
|
|
||||||
|
public GameProfile() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile(UUID id, String name) {
|
||||||
|
this(id, name, new PropertyMap(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile(UUID id, String name, PropertyMap properties, boolean legacy) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.properties = properties;
|
||||||
|
this.legacy = legacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyMap getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLegacy() {
|
||||||
|
return legacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Serializer implements JsonSerializer<GameProfile>, JsonDeserializer<GameProfile> {
|
||||||
|
|
||||||
|
public static final Serializer INSTANCE = new Serializer();
|
||||||
|
|
||||||
|
private Serializer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(GameProfile src, Type type, JsonSerializationContext context) {
|
||||||
|
JsonObject result = new JsonObject();
|
||||||
|
if (src.getId() != null)
|
||||||
|
result.add("id", context.serialize(src.getId()));
|
||||||
|
if (src.getName() != null)
|
||||||
|
result.addProperty("name", src.getName());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameProfile deserialize(JsonElement je, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
if (!(je instanceof JsonObject))
|
||||||
|
throw new JsonParseException("The json element is not a JsonObject.");
|
||||||
|
|
||||||
|
JsonObject json = (JsonObject) je;
|
||||||
|
|
||||||
|
UUID id = json.has("id") ? context.deserialize(json.get("id"), UUID.class) : null;
|
||||||
|
String name = json.has("name") ? json.getAsJsonPrimitive("name").getAsString() : null;
|
||||||
|
return new GameProfile(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class InvalidCredentialsException extends AuthenticationException {
|
||||||
|
|
||||||
|
private final YggdrasilAccount account;
|
||||||
|
|
||||||
|
public InvalidCredentialsException(YggdrasilAccount account) {
|
||||||
|
this.account = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public YggdrasilAccount getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,20 +15,24 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
enum class UserType() {
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
LEGACY,
|
|
||||||
MOJANG;
|
|
||||||
|
|
||||||
companion object {
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class InvalidTokenException extends AuthenticationException {
|
||||||
|
|
||||||
fun fromName(name: String) = BY_NAME[name.toLowerCase()]
|
private YggdrasilAccount account;
|
||||||
fun fromLegacy(isLegacy: Boolean) = if (isLegacy) LEGACY else MOJANG
|
|
||||||
|
|
||||||
private val BY_NAME = HashMap<String, UserType>().apply {
|
public InvalidTokenException(YggdrasilAccount account) {
|
||||||
for (type in values())
|
super();
|
||||||
this[type.name.toLowerCase()] = type
|
this.account = account;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public YggdrasilAccount getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Hello Minecraft! Launcher.
|
* Hello Minecraft! Launcher.
|
||||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@@ -15,12 +15,23 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap
|
public class Property {
|
||||||
|
|
||||||
internal class SimpleTask @JvmOverloads constructor(private val runnable: (AutoTypingMap<String>) -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
|
private final String name;
|
||||||
override fun execute() {
|
private final String value;
|
||||||
runnable(variables!!)
|
|
||||||
|
public Property(String name, String value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
|
||||||
|
public final class PropertyMap extends HashMap<String, Property> {
|
||||||
|
|
||||||
|
public List<Map<String, String>> toList() {
|
||||||
|
List<Map<String, String>> properties = new ArrayList<>();
|
||||||
|
for (Property profileProperty : values()) {
|
||||||
|
Map<String, String> property = new HashMap<>();
|
||||||
|
property.put("name", profileProperty.getName());
|
||||||
|
property.put("value", profileProperty.getValue());
|
||||||
|
properties.add(property);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fromList(List<?> list) {
|
||||||
|
for (Object propertyMap : list) {
|
||||||
|
if (!(propertyMap instanceof Map<?, ?>))
|
||||||
|
continue;
|
||||||
|
Optional<String> name = Lang.get((Map<?, ?>) propertyMap, "name", String.class);
|
||||||
|
Optional<String> value = Lang.get((Map<?, ?>) propertyMap, "value", String.class);
|
||||||
|
if (name.isPresent() && value.isPresent())
|
||||||
|
put(name.get(), new Property(name.get(), value.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Serializer implements JsonSerializer<PropertyMap>, JsonDeserializer<PropertyMap> {
|
||||||
|
|
||||||
|
public static final Serializer INSTANCE = new Serializer();
|
||||||
|
|
||||||
|
private Serializer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PropertyMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
PropertyMap result = new PropertyMap();
|
||||||
|
if (json instanceof JsonObject) {
|
||||||
|
for (Map.Entry<String, JsonElement> entry : ((JsonObject) json).entrySet())
|
||||||
|
if (entry.getValue() instanceof JsonArray)
|
||||||
|
for (JsonElement element : (JsonArray) entry.getValue())
|
||||||
|
result.put(entry.getKey(), new Property(entry.getKey(), element.getAsString()));
|
||||||
|
} else if ((json instanceof JsonArray))
|
||||||
|
for (JsonElement element : (JsonArray) json)
|
||||||
|
if ((element instanceof JsonObject)) {
|
||||||
|
JsonObject object = (JsonObject) element;
|
||||||
|
String name = object.getAsJsonPrimitive("name").getAsString();
|
||||||
|
String value = object.getAsJsonPrimitive("value").getAsString();
|
||||||
|
result.put(name, new Property(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonArray result = new JsonArray();
|
||||||
|
for (Property property : src.values()) {
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
object.addProperty("name", property.getName());
|
||||||
|
object.addProperty("value", property.getValue());
|
||||||
|
result.add(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LegacySerializer
|
||||||
|
implements JsonSerializer<PropertyMap> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonObject result = new JsonObject();
|
||||||
|
for (PropertyMap.Entry<String, Property> entry : src.entrySet()) {
|
||||||
|
JsonArray values = new JsonArray();
|
||||||
|
values.add(new JsonPrimitive(entry.getValue().getValue()));
|
||||||
|
result.add(entry.getKey(), values);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public final class RefreshRequest {
|
||||||
|
|
||||||
|
private final String accessToken;
|
||||||
|
private final String clientToken;
|
||||||
|
private final GameProfile selectedProfile;
|
||||||
|
private final boolean requestUser;
|
||||||
|
|
||||||
|
public RefreshRequest(String accessToken, String clientToken) {
|
||||||
|
this(accessToken, clientToken, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile) {
|
||||||
|
this(accessToken, clientToken, selectedProfile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshRequest(String accessToken, String clientToken, GameProfile selectedProfile, boolean requestUser) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.clientToken = clientToken;
|
||||||
|
this.selectedProfile = selectedProfile;
|
||||||
|
this.requestUser = requestUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile getSelectedProfile() {
|
||||||
|
return selectedProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestUser() {
|
||||||
|
return requestUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class Response {
|
||||||
|
|
||||||
|
private final String accessToken;
|
||||||
|
private final String clientToken;
|
||||||
|
private final GameProfile selectedProfile;
|
||||||
|
private final GameProfile[] availableProfiles;
|
||||||
|
private final User user;
|
||||||
|
private final String error;
|
||||||
|
private final String errorMessage;
|
||||||
|
private final String cause;
|
||||||
|
|
||||||
|
public Response() {
|
||||||
|
this(null, null, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response(String accessToken, String clientToken, GameProfile selectedProfile, GameProfile[] availableProfiles, User user, String error, String errorMessage, String cause) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.clientToken = clientToken;
|
||||||
|
this.selectedProfile = selectedProfile;
|
||||||
|
this.availableProfiles = availableProfiles;
|
||||||
|
this.user = user;
|
||||||
|
this.error = error;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile getSelectedProfile() {
|
||||||
|
return selectedProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile[] getAvailableProfiles() {
|
||||||
|
return availableProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCause() {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.Validation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public final class User implements Validation {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final PropertyMap properties;
|
||||||
|
|
||||||
|
public User(String id) {
|
||||||
|
this(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String id, PropertyMap properties) {
|
||||||
|
this.id = id;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyMap getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException {
|
||||||
|
if (StringUtils.isBlank(id))
|
||||||
|
throw new JsonParseException("User id cannot be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,19 +15,28 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap
|
/**
|
||||||
import java.util.concurrent.Callable
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class ValidateRequest {
|
||||||
|
|
||||||
internal class TaskCallable<V>(override val id: String, private val callable: Callable<V>) : TaskResult<V>() {
|
private final String accessToken;
|
||||||
override fun execute() {
|
private final String clientToken;
|
||||||
result = callable.call()
|
|
||||||
|
public ValidateRequest(String accessToken, String clientToken) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.clientToken = clientToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TaskCallable2<V>(override val id: String, private val callable: (AutoTypingMap<String>) -> V) : TaskResult<V>() {
|
|
||||||
override fun execute() {
|
|
||||||
result = callable(variables!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.jackhuang.hmcl.auth.Account;
|
||||||
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
import org.jackhuang.hmcl.auth.UserType;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public final class YggdrasilAccount extends Account {
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private String password;
|
||||||
|
private String userId;
|
||||||
|
private String accessToken = null;
|
||||||
|
private String clientToken = randomToken();
|
||||||
|
private boolean isOnline = false;
|
||||||
|
private PropertyMap userProperties = new PropertyMap();
|
||||||
|
private GameProfile selectedProfile = null;
|
||||||
|
private GameProfile[] profiles;
|
||||||
|
private UserType userType = UserType.LEGACY;
|
||||||
|
|
||||||
|
public YggdrasilAccount(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessToken(String accessToken) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getClientToken() {
|
||||||
|
return clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setClientToken(String clientToken) {
|
||||||
|
this.clientToken = clientToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyMap getUserProperties() {
|
||||||
|
return userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameProfile getSelectedProfile() {
|
||||||
|
return selectedProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedProfile(GameProfile selectedProfile) {
|
||||||
|
this.selectedProfile = selectedProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoggedIn() {
|
||||||
|
return StringUtils.isNotBlank(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canPlayOnline() {
|
||||||
|
return isLoggedIn() && selectedProfile != null && isOnline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canLogIn() {
|
||||||
|
return !canPlayOnline() && StringUtils.isNotBlank(username)
|
||||||
|
&& (StringUtils.isNotBlank(password) || StringUtils.isNotBlank(accessToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthInfo logIn(Proxy proxy) throws AuthenticationException {
|
||||||
|
if (canPlayOnline())
|
||||||
|
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
|
||||||
|
else {
|
||||||
|
logIn0(proxy);
|
||||||
|
if (!isLoggedIn())
|
||||||
|
throw new AuthenticationException("Wrong password for account " + username);
|
||||||
|
|
||||||
|
if (selectedProfile == null)
|
||||||
|
// TODO: multi-available-profiles support
|
||||||
|
throw new UnsupportedOperationException("Do not support multi-available-profiles account yet.");
|
||||||
|
else
|
||||||
|
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logIn0(Proxy proxy) throws AuthenticationException {
|
||||||
|
if (StringUtils.isNotBlank(accessToken)) {
|
||||||
|
if (StringUtils.isBlank(userId))
|
||||||
|
if (StringUtils.isNotBlank(username))
|
||||||
|
userId = username;
|
||||||
|
else
|
||||||
|
throw new AuthenticationException("Invalid uuid and username");
|
||||||
|
if (checkTokenValidity(proxy)) {
|
||||||
|
isOnline = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logIn1(ROUTE_REFRESH, new RefreshRequest(accessToken, clientToken), proxy);
|
||||||
|
} else if (StringUtils.isNotBlank(password))
|
||||||
|
logIn1(ROUTE_AUTHENTICATE, new AuthenticationRequest(username, password, clientToken), proxy);
|
||||||
|
else
|
||||||
|
throw new AuthenticationException("Password cannot be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logIn1(URL url, Object input, Proxy proxy) throws AuthenticationException {
|
||||||
|
Response response = makeRequest(url, input, proxy);
|
||||||
|
if (response == null || !clientToken.equals(response.getClientToken()))
|
||||||
|
throw new AuthenticationException("Client token changed");
|
||||||
|
|
||||||
|
if (response.getSelectedProfile() != null)
|
||||||
|
userType = UserType.fromLegacy(response.getSelectedProfile().isLegacy());
|
||||||
|
else if (response.getAvailableProfiles() != null && response.getAvailableProfiles().length > 0)
|
||||||
|
userType = UserType.fromLegacy(response.getAvailableProfiles()[0].isLegacy());
|
||||||
|
|
||||||
|
User user = response.getUser();
|
||||||
|
if (user == null || user.getId() == null)
|
||||||
|
userId = null;
|
||||||
|
else
|
||||||
|
userId = user.getId();
|
||||||
|
|
||||||
|
isOnline = true;
|
||||||
|
profiles = response.getAvailableProfiles();
|
||||||
|
selectedProfile = response.getSelectedProfile();
|
||||||
|
userProperties.clear();
|
||||||
|
accessToken = response.getAccessToken();
|
||||||
|
|
||||||
|
if (user != null && user.getProperties() != null)
|
||||||
|
userProperties.putAll(user.getProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logOut() {
|
||||||
|
password = null;
|
||||||
|
userId = null;
|
||||||
|
accessToken = null;
|
||||||
|
isOnline = false;
|
||||||
|
userProperties.clear();
|
||||||
|
profiles = null;
|
||||||
|
selectedProfile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Object, Object> toStorage() {
|
||||||
|
HashMap<Object, Object> result = new HashMap<>();
|
||||||
|
|
||||||
|
result.put(STORAGE_KEY_USER_NAME, getUsername());
|
||||||
|
result.put(STORAGE_KEY_CLIENT_TOKEN, getClientToken());
|
||||||
|
if (getUserId() != null)
|
||||||
|
result.put(STORAGE_KEY_USER_ID, getUserId());
|
||||||
|
if (!userProperties.isEmpty())
|
||||||
|
result.put(STORAGE_KEY_USER_PROPERTIES, userProperties.toList());
|
||||||
|
GameProfile profile = selectedProfile;
|
||||||
|
if (profile != null && profile.getName() != null && profile.getId() != null) {
|
||||||
|
result.put(STORAGE_KEY_PROFILE_NAME, profile.getName());
|
||||||
|
result.put(STORAGE_KEY_PROFILE_ID, profile.getId());
|
||||||
|
|
||||||
|
if (!profile.getProperties().isEmpty())
|
||||||
|
result.put(STORAGE_KEY_PROFILE_PROPERTIES, profile.getProperties().toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(accessToken))
|
||||||
|
result.put(STORAGE_KEY_ACCESS_TOKEN, accessToken);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response makeRequest(URL url, Object input, Proxy proxy) throws AuthenticationException {
|
||||||
|
try {
|
||||||
|
String jsonResult = input == null ? NetworkUtils.doGet(url, proxy) : NetworkUtils.doPost(url, GSON.toJson(input), "application/json", proxy);
|
||||||
|
Response response = GSON.fromJson(jsonResult, Response.class);
|
||||||
|
if (response == null)
|
||||||
|
return null;
|
||||||
|
if (!StringUtils.isBlank(response.getError())) {
|
||||||
|
if (response.getErrorMessage() != null)
|
||||||
|
if (response.getErrorMessage().contains("Invalid credentials"))
|
||||||
|
throw new InvalidCredentialsException(this);
|
||||||
|
else if (response.getErrorMessage().contains("Invalid token"))
|
||||||
|
throw new InvalidTokenException(this);
|
||||||
|
throw new AuthenticationException(response.getError() + ": " + response.getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AuthenticationException("Unable to connect to authentication server", e);
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
throw new AuthenticationException("Unable to parse server response", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkTokenValidity(Proxy proxy) {
|
||||||
|
if (accessToken == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
makeRequest(ROUTE_VALIDATE, new ValidateRequest(accessToken, clientToken), proxy);
|
||||||
|
return true;
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "YggdrasilAccount[username=" + getUsername() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String BASE_URL = "https://authserver.mojang.com/";
|
||||||
|
private static final URL ROUTE_AUTHENTICATE = NetworkUtils.toURL(BASE_URL + "authenticate");
|
||||||
|
private static final URL ROUTE_REFRESH = NetworkUtils.toURL(BASE_URL + "refresh");
|
||||||
|
private static final URL ROUTE_VALIDATE = NetworkUtils.toURL(BASE_URL + "validate");
|
||||||
|
|
||||||
|
static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
|
||||||
|
static final String STORAGE_KEY_PROFILE_NAME = "displayName";
|
||||||
|
static final String STORAGE_KEY_PROFILE_ID = "uuid";
|
||||||
|
static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
|
||||||
|
static final String STORAGE_KEY_USER_NAME = "username";
|
||||||
|
static final String STORAGE_KEY_USER_ID = "userid";
|
||||||
|
static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
|
||||||
|
static final String STORAGE_KEY_CLIENT_TOKEN = "clientToken";
|
||||||
|
|
||||||
|
public static String randomToken() {
|
||||||
|
return UUIDTypeAdapter.fromUUID(UUID.randomUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Gson GSON = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(GameProfile.class, GameProfile.Serializer.INSTANCE)
|
||||||
|
.registerTypeAdapter(PropertyMap.class, PropertyMap.Serializer.INSTANCE)
|
||||||
|
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.yggdrasil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
import static org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount.*;
|
||||||
|
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class YggdrasilAccountFactory extends AccountFactory<YggdrasilAccount> {
|
||||||
|
public static final YggdrasilAccountFactory INSTANCE = new YggdrasilAccountFactory();
|
||||||
|
|
||||||
|
private YggdrasilAccountFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public YggdrasilAccount fromUsername(String username, String password) {
|
||||||
|
YggdrasilAccount account = new YggdrasilAccount(username);
|
||||||
|
account.setPassword(password);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public YggdrasilAccount fromStorage(Map<Object, Object> storage) {
|
||||||
|
String username = Lang.get(storage, STORAGE_KEY_USER_NAME, String.class)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_USER_NAME));
|
||||||
|
|
||||||
|
YggdrasilAccount account = new YggdrasilAccount(username);
|
||||||
|
account.setUserId(Lang.get(storage, STORAGE_KEY_USER_ID, String.class, username));
|
||||||
|
account.setAccessToken(Lang.get(storage, STORAGE_KEY_ACCESS_TOKEN, String.class, null));
|
||||||
|
account.setClientToken(Lang.get(storage, STORAGE_KEY_CLIENT_TOKEN, String.class)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("storage does not have key " + STORAGE_KEY_CLIENT_TOKEN)));
|
||||||
|
|
||||||
|
Lang.get(storage, STORAGE_KEY_USER_PROPERTIES, List.class)
|
||||||
|
.ifPresent(account.getUserProperties()::fromList);
|
||||||
|
Optional<String> profileId = Lang.get(storage, STORAGE_KEY_PROFILE_ID, String.class);
|
||||||
|
Optional<String> profileName = Lang.get(storage, STORAGE_KEY_PROFILE_NAME, String.class);
|
||||||
|
GameProfile profile = null;
|
||||||
|
if (profileId.isPresent() && profileName.isPresent()) {
|
||||||
|
profile = new GameProfile(UUIDTypeAdapter.fromString(profileId.get()), profileName.get());
|
||||||
|
Lang.get(storage, STORAGE_KEY_PROFILE_PROPERTIES, List.class)
|
||||||
|
.ifPresent(profile.getProperties()::fromList);
|
||||||
|
}
|
||||||
|
account.setSelectedProfile(profile);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,14 +15,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
abstract class AbstractDependencyManager
|
/**
|
||||||
: DependencyManager {
|
*
|
||||||
abstract val downloadProvider: DownloadProvider
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public abstract class AbstractDependencyManager implements DependencyManager {
|
||||||
|
|
||||||
override fun getVersionList(id: String): VersionList<*> {
|
public abstract DownloadProvider getDownloadProvider();
|
||||||
return downloadProvider.getVersionListById(id)
|
|
||||||
|
@Override
|
||||||
|
public VersionList<?> getVersionList(String id) {
|
||||||
|
return getDownloadProvider().getVersionListById(id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||||
|
|
||||||
|
public static final BMCLAPIDownloadProvider INSTANCE = new BMCLAPIDownloadProvider();
|
||||||
|
|
||||||
|
private BMCLAPIDownloadProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLibraryBaseURL() {
|
||||||
|
return "http://bmclapi2.bangbang93.com/libraries/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVersionListURL() {
|
||||||
|
return "http://bmclapi2.bangbang93.com/mc/game/version_manifest.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVersionBaseURL() {
|
||||||
|
return "http://bmclapi2.bangbang93.com/versions/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAssetIndexBaseURL() {
|
||||||
|
return "http://bmclapi2.bangbang93.com/indexes/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAssetBaseURL() {
|
||||||
|
return "http://bmclapi2.bangbang93.com/assets/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionList<?> getVersionListById(String id) {
|
||||||
|
switch (id) {
|
||||||
|
case "game":
|
||||||
|
return GameVersionList.INSTANCE;
|
||||||
|
case "forge":
|
||||||
|
return ForgeVersionList.INSTANCE;
|
||||||
|
case "liteloader":
|
||||||
|
return LiteLoaderVersionList.INSTANCE;
|
||||||
|
case "optifine":
|
||||||
|
return OptiFineBMCLVersionList.INSTANCE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unrecognized version list id: " + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String injectURL(String baseURL) {
|
||||||
|
return baseURL
|
||||||
|
.replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||||
|
.replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||||
|
.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||||
|
.replace("http://files.minecraftforge.net/maven", "https://bmclapi2.bangbang93.com/maven")
|
||||||
|
.replace("http://dl.liteloader.com/versions/versions.json", "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
|
||||||
|
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
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;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: This class has no state.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||||
|
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final DownloadProvider downloadProvider;
|
||||||
|
private final Proxy proxy;
|
||||||
|
|
||||||
|
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider) {
|
||||||
|
this(repository, downloadProvider, Proxy.NO_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, Proxy proxy) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.downloadProvider = downloadProvider;
|
||||||
|
this.proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultGameRepository getGameRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadProvider getDownloadProvider() {
|
||||||
|
return downloadProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Proxy getProxy() {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameBuilder gameBuilder() {
|
||||||
|
return new DefaultGameBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task checkGameCompletionAsync(Version version) {
|
||||||
|
return new ParallelTask(
|
||||||
|
new GameAssetDownloadTask(this, version),
|
||||||
|
new GameLoggingDownloadTask(this, version),
|
||||||
|
new GameLibrariesTask(this, version)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
|
||||||
|
switch (libraryId) {
|
||||||
|
case "forge":
|
||||||
|
return new ForgeInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
|
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
||||||
|
case "liteloader":
|
||||||
|
return new LiteLoaderInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
|
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
||||||
|
case "optifine":
|
||||||
|
return new OptiFineInstallTask(this, gameVersion, version, libraryVersion)
|
||||||
|
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Library id " + libraryId + " is unrecognized.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLoggingDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.ParallelTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||||
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class DefaultGameBuilder extends GameBuilder {
|
||||||
|
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final DownloadProvider downloadProvider;
|
||||||
|
|
||||||
|
public DefaultGameBuilder(DefaultDependencyManager dependencyManager) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.downloadProvider = dependencyManager.getDownloadProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task buildAsync() {
|
||||||
|
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
|
||||||
|
Version version = Constants.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
|
||||||
|
variables.set("version", version);
|
||||||
|
version = version.setId(name).setJar(null);
|
||||||
|
Task result = new ParallelTask(
|
||||||
|
new GameAssetDownloadTask(dependencyManager, version),
|
||||||
|
new GameLoggingDownloadTask(dependencyManager, version),
|
||||||
|
new GameDownloadTask(dependencyManager, version),
|
||||||
|
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
|
||||||
|
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant.
|
||||||
|
|
||||||
|
if (toolVersions.containsKey("forge"))
|
||||||
|
result = result.then(libraryTaskHelper(gameVersion, "forge"));
|
||||||
|
if (toolVersions.containsKey("liteloader"))
|
||||||
|
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
|
||||||
|
if (toolVersions.containsKey("optifine"))
|
||||||
|
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<AutoTypingMap<String>, Task> libraryTaskHelper(String gameVersion, String libraryId) {
|
||||||
|
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,27 +15,30 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.game.GameRepository
|
import java.net.Proxy;
|
||||||
import org.jackhuang.hmcl.game.Version
|
import org.jackhuang.hmcl.game.GameRepository;
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import java.net.Proxy
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do everything that will connect to Internet.
|
* Do everything that will connect to Internet.
|
||||||
* Downloading Minecraft files.
|
* Downloading Minecraft files.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
interface DependencyManager {
|
public interface DependencyManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The relied game repository.
|
* The relied game repository.
|
||||||
*/
|
*/
|
||||||
val repository: GameRepository
|
GameRepository getGameRepository();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The proxy that all network operations should go through.
|
* The proxy that all network operations should go through.
|
||||||
*/
|
*/
|
||||||
val proxy: Proxy
|
Proxy getProxy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the game is complete.
|
* Check if the game is complete.
|
||||||
@@ -43,12 +46,12 @@ interface DependencyManager {
|
|||||||
*
|
*
|
||||||
* @return the task to check game completion.
|
* @return the task to check game completion.
|
||||||
*/
|
*/
|
||||||
fun checkGameCompletionAsync(version: Version): Task
|
Task checkGameCompletionAsync(Version version);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
|
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
|
||||||
*/
|
*/
|
||||||
fun gameBuilder(): GameBuilder
|
GameBuilder gameBuilder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a library to a version.
|
* Install a library to a version.
|
||||||
@@ -60,7 +63,7 @@ interface DependencyManager {
|
|||||||
* @param libraryVersion the version of being installed library.
|
* @param libraryVersion the version of being installed library.
|
||||||
* @return the task to install the specific library.
|
* @return the task to install the specific library.
|
||||||
*/
|
*/
|
||||||
fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
|
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get registered version list.
|
* Get registered version list.
|
||||||
@@ -68,5 +71,5 @@ interface DependencyManager {
|
|||||||
* @param id the id of version list. i.e. game, forge, liteloader, optifine
|
* @param id the id of version list. i.e. game, forge, liteloader, optifine
|
||||||
* @throws IllegalArgumentException if the version list of specific id is not found.
|
* @throws IllegalArgumentException if the version list of specific id is not found.
|
||||||
*/
|
*/
|
||||||
fun getVersionList(id: String): VersionList<*>
|
VersionList<?> getVersionList(String id);
|
||||||
}
|
}
|
||||||
@@ -15,17 +15,24 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service provider that provides Minecraft online file downloads.
|
* The service provider that provides Minecraft online file downloads.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
interface DownloadProvider {
|
public interface DownloadProvider {
|
||||||
val libraryBaseURL: String
|
|
||||||
val versionListURL: String
|
String getLibraryBaseURL();
|
||||||
val versionBaseURL: String
|
|
||||||
val assetIndexBaseURL: String
|
String getVersionListURL();
|
||||||
val assetBaseURL: String
|
|
||||||
|
String getVersionBaseURL();
|
||||||
|
|
||||||
|
String getAssetIndexBaseURL();
|
||||||
|
|
||||||
|
String getAssetBaseURL();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject into original URL provided by Mojang and Forge.
|
* Inject into original URL provided by Mojang and Forge.
|
||||||
@@ -36,12 +43,13 @@ interface DownloadProvider {
|
|||||||
* @param baseURL original URL provided by Mojang and Forge.
|
* @param baseURL original URL provided by Mojang and Forge.
|
||||||
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
||||||
*/
|
*/
|
||||||
fun injectURL(baseURL: String): String
|
String injectURL(String baseURL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
* 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"
|
* @param id the id of specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||||
* @return the version list
|
* @return the version list
|
||||||
*/
|
*/
|
||||||
fun getVersionListById(id: String): VersionList<*>
|
VersionList<?> getVersionListById(String id);
|
||||||
}
|
}
|
||||||
@@ -15,45 +15,57 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The builder which provide a task to build Minecraft environment.
|
* The builder which provide a task to build Minecraft environment.
|
||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
abstract class GameBuilder {
|
public abstract class GameBuilder {
|
||||||
var name: String = ""
|
|
||||||
protected var gameVersion: String = ""
|
protected String name = "";
|
||||||
protected var toolVersions = HashMap<String, String>()
|
protected String gameVersion = "";
|
||||||
|
protected Map<String, String> toolVersions = new HashMap<>();
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The new game version name, for .minecraft/<version name>.
|
* The new game version name, for .minecraft/<version name>.
|
||||||
|
*
|
||||||
* @param name the name of new game version.
|
* @param name the name of new game version.
|
||||||
*/
|
*/
|
||||||
fun name(name: String): GameBuilder {
|
public GameBuilder name(String name) {
|
||||||
this.name = name
|
this.name = Objects.requireNonNull(name);
|
||||||
return this
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gameVersion(version: String): GameBuilder {
|
public GameBuilder gameVersion(String version) {
|
||||||
gameVersion = version
|
this.gameVersion = Objects.requireNonNull(version);
|
||||||
return this
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id the core library id. i.e. "forge", "liteloader", "optifine"
|
* @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]
|
* @param version the version of the core library. For documents, you can first try [VersionList.versions]
|
||||||
*/
|
*/
|
||||||
fun version(id: String, version: String): GameBuilder {
|
public GameBuilder version(String id, String version) {
|
||||||
toolVersions[id] = version
|
if ("game".equals(id))
|
||||||
return this
|
gameVersion(version);
|
||||||
|
else
|
||||||
|
toolVersions.put(id, version);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the task that can build thw whole Minecraft environment
|
* @return the task that can build thw whole Minecraft environment
|
||||||
*/
|
*/
|
||||||
abstract fun buildAsync(): Task
|
public abstract Task buildAsync();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see {@link http://wiki.vg}
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class MojangDownloadProvider implements DownloadProvider {
|
||||||
|
|
||||||
|
public static final MojangDownloadProvider INSTANCE = new MojangDownloadProvider();
|
||||||
|
|
||||||
|
private MojangDownloadProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLibraryBaseURL() {
|
||||||
|
return "https://libraries.minecraft.net/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVersionListURL() {
|
||||||
|
return "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getVersionBaseURL() {
|
||||||
|
return "http://s3.amazonaws.com/Minecraft.Download/versions/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAssetIndexBaseURL() {
|
||||||
|
return "http://s3.amazonaws.com/Minecraft.Download/indexes/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAssetBaseURL() {
|
||||||
|
return "http://resources.download.minecraft.net/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionList<?> getVersionListById(String id) {
|
||||||
|
switch (id) {
|
||||||
|
case "game":
|
||||||
|
return GameVersionList.INSTANCE;
|
||||||
|
case "forge":
|
||||||
|
return ForgeVersionList.INSTANCE;
|
||||||
|
case "liteloader":
|
||||||
|
return LiteLoaderVersionList.INSTANCE;
|
||||||
|
case "optifine":
|
||||||
|
return OptiFineVersionList.INSTANCE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unrecognized version list id: " + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String injectURL(String baseURL) {
|
||||||
|
if (baseURL.contains("net/minecraftforge/forge"))
|
||||||
|
return baseURL;
|
||||||
|
else
|
||||||
|
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote version.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
|
||||||
|
|
||||||
|
private final String gameVersion;
|
||||||
|
private final String selfVersion;
|
||||||
|
private final String url;
|
||||||
|
private final T tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public RemoteVersion(String gameVersion, String selfVersion, String url, T tag) {
|
||||||
|
this.gameVersion = Objects.requireNonNull(gameVersion);
|
||||||
|
this.selfVersion = Objects.requireNonNull(selfVersion);
|
||||||
|
this.url = Objects.requireNonNull(url);
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelfVersion() {
|
||||||
|
return selfVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof RemoteVersion && Objects.equals(selfVersion, ((RemoteVersion) obj).selfVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return selfVersion.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(RemoteVersion<T> o) {
|
||||||
|
// newer versions are smaller than older versions
|
||||||
|
return -selfVersion.compareTo(o.selfVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RemoteVersionComparator implements Comparator<RemoteVersion<?>> {
|
||||||
|
|
||||||
|
public static final RemoteVersionComparator INSTANCE = new RemoteVersionComparator();
|
||||||
|
|
||||||
|
private RemoteVersionComparator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(RemoteVersion<?> o1, RemoteVersion<?> o2) {
|
||||||
|
return -VersionNumber.asVersion(o1.selfVersion).compareTo(VersionNumber.asVersion(o2.selfVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,39 +15,48 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
import java.util.Collection;
|
||||||
import org.jackhuang.hmcl.util.SimpleMultimap
|
import java.util.Collections;
|
||||||
import java.util.*
|
import java.util.HashMap;
|
||||||
import kotlin.collections.HashMap
|
import java.util.Optional;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The remote version list.
|
* The remote version list.
|
||||||
* @param T The type of RemoteVersion<T>, the type of tags.
|
*
|
||||||
|
* @param T The type of {@code RemoteVersion<T>}, the type of tags.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
abstract class VersionList<T> {
|
public abstract class VersionList<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the remote version list.
|
* the remote version list.
|
||||||
* key: game version.
|
* key: game version.
|
||||||
* values: corresponding remote versions.
|
* values: corresponding remote versions.
|
||||||
*/
|
*/
|
||||||
protected val versions = SimpleMultimap<String, RemoteVersion<T>>(::HashMap, ::TreeSet)
|
protected final SimpleMultimap<String, RemoteVersion<T>> versions = new SimpleMultimap<String, RemoteVersion<T>>(HashMap::new, TreeSet::new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the version list has been loaded.
|
* True if the version list has been loaded.
|
||||||
*/
|
*/
|
||||||
val loaded = versions.isNotEmpty
|
public boolean isLoaded() {
|
||||||
|
return !versions.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param downloadProvider DownloadProvider
|
* @param downloadProvider DownloadProvider
|
||||||
* @return the task to reload the remote version list.
|
* @return the task to reload the remote version list.
|
||||||
*/
|
*/
|
||||||
abstract fun refreshAsync(downloadProvider: DownloadProvider): Task
|
public abstract Task refreshAsync(DownloadProvider downloadProvider);
|
||||||
|
|
||||||
private fun getVersionsImpl(gameVersion: String): Collection<RemoteVersion<T>> {
|
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
|
||||||
val ans = versions[gameVersion]
|
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
|
||||||
return if (ans.isEmpty()) versions.values else ans
|
return ans.isEmpty() ? versions.values() : ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,8 +65,8 @@ abstract class VersionList<T> {
|
|||||||
* @param gameVersion the Minecraft version that remote versions belong to
|
* @param gameVersion the Minecraft version that remote versions belong to
|
||||||
* @return the collection of specific remote versions
|
* @return the collection of specific remote versions
|
||||||
*/
|
*/
|
||||||
fun getVersions(gameVersion: String): Collection<RemoteVersion<T>> {
|
public final Collection<RemoteVersion<T>> getVersions(String gameVersion) {
|
||||||
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion))
|
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,13 +76,11 @@ abstract class VersionList<T> {
|
|||||||
* @param remoteVersion the version of the remote version.
|
* @param remoteVersion the version of the remote version.
|
||||||
* @return the specific remote version, null if it is not found.
|
* @return the specific remote version, null if it is not found.
|
||||||
*/
|
*/
|
||||||
fun getVersion(gameVersion: String, remoteVersion: String): RemoteVersion<T>? {
|
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
|
||||||
var result : RemoteVersion<T>? = null
|
RemoteVersion<T> result = null;
|
||||||
versions[gameVersion].forEach {
|
for (RemoteVersion<T> it : versions.get(gameVersion))
|
||||||
if (it.selfVersion == remoteVersion)
|
if (remoteVersion.equals(it.getSelfVersion()))
|
||||||
result = it
|
result = it;
|
||||||
}
|
return Optional.ofNullable(result);
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.VersionList;
|
||||||
|
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;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
|
import org.jackhuang.hmcl.util.IOUtils;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class ForgeInstallTask extends TaskResult<Version> {
|
||||||
|
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final String gameVersion;
|
||||||
|
private final Version version;
|
||||||
|
private final String remoteVersion;
|
||||||
|
private final VersionList<?> forgeVersionList;
|
||||||
|
private final File installer = new File("forge-installer.jar").getAbsoluteFile();
|
||||||
|
private RemoteVersion<?> remote;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
private Task downloadFileTask() {
|
||||||
|
remote = forgeVersionList.getVersion(gameVersion, remoteVersion)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Remote forge version " + gameVersion + ", " + remoteVersion + " not found"));
|
||||||
|
return new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForgeInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.version = version;
|
||||||
|
this.remoteVersion = remoteVersion;
|
||||||
|
|
||||||
|
forgeVersionList = dependencyManager.getVersionList("forge");
|
||||||
|
|
||||||
|
if (!forgeVersionList.isLoaded())
|
||||||
|
dependents.add(forgeVersionList.refreshAsync(dependencyManager.getDownloadProvider())
|
||||||
|
.then(s -> downloadFileTask()));
|
||||||
|
else
|
||||||
|
dependents.add(downloadFileTask());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "version";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRelyingOnDependencies() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
try (ZipFile zipFile = new ZipFile(installer)) {
|
||||||
|
InputStream stream = zipFile.getInputStream(zipFile.getEntry("install_profile.json"));
|
||||||
|
if (stream == null)
|
||||||
|
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");
|
||||||
|
String json = IOUtils.readFullyAsString(stream);
|
||||||
|
InstallProfile installProfile = Constants.GSON.fromJson(json, InstallProfile.class);
|
||||||
|
if (installProfile == null)
|
||||||
|
throw new IOException("Malformed forge installer file, install_profile.json does not exist.");
|
||||||
|
|
||||||
|
// unpack the universal jar in the installer file.
|
||||||
|
Library forgeLibrary = Library.fromName(installProfile.getInstall().getPath());
|
||||||
|
File forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary);
|
||||||
|
if (!FileUtils.makeFile(forgeFile))
|
||||||
|
throw new IOException("Cannot make directory " + forgeFile.getParent());
|
||||||
|
|
||||||
|
ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath());
|
||||||
|
try (InputStream is = zipFile.getInputStream(forgeEntry); OutputStream os = new FileOutputStream(forgeFile)) {
|
||||||
|
IOUtils.copyTo(is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve the version
|
||||||
|
SimpleVersionProvider provider = new SimpleVersionProvider();
|
||||||
|
provider.addVersion(version);
|
||||||
|
|
||||||
|
setResult(installProfile.getVersionInfo()
|
||||||
|
.setInheritsFrom(version.getId())
|
||||||
|
.resolve(provider)
|
||||||
|
.setId(version.getId()).setLogging(Collections.EMPTY_MAP));
|
||||||
|
|
||||||
|
dependencies.add(new GameLibrariesTask(dependencyManager, installProfile.getVersionInfo()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!installer.delete())
|
||||||
|
throw new IOException("Unable to delete installer file" + installer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
import org.jackhuang.hmcl.util.Validation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class ForgeVersion implements Validation {
|
||||||
|
|
||||||
|
private final String branch;
|
||||||
|
private final String mcversion;
|
||||||
|
private final String jobver;
|
||||||
|
private final String version;
|
||||||
|
private final int build;
|
||||||
|
private final double modified;
|
||||||
|
private final String[][] files;
|
||||||
|
|
||||||
|
public ForgeVersion() {
|
||||||
|
this(null, null, null, null, 0, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForgeVersion(String branch, String mcversion, String jobver, String version, int build, double modified, String[][] files) {
|
||||||
|
this.branch = branch;
|
||||||
|
this.mcversion = mcversion;
|
||||||
|
this.jobver = jobver;
|
||||||
|
this.version = version;
|
||||||
|
this.build = build;
|
||||||
|
this.modified = modified;
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBranch() {
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return mcversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJobver() {
|
||||||
|
return jobver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBuild() {
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getModified() {
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[][] getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException {
|
||||||
|
if (files == null)
|
||||||
|
throw new JsonParseException("ForgeVersion files cannot be null");
|
||||||
|
if (version == null)
|
||||||
|
throw new JsonParseException("ForgeVersion version cannot be null");
|
||||||
|
if (mcversion == null)
|
||||||
|
throw new JsonParseException("ForgeVersion mcversion cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
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.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class ForgeVersionList extends VersionList<Void> {
|
||||||
|
|
||||||
|
public static final ForgeVersionList INSTANCE = new ForgeVersionList();
|
||||||
|
|
||||||
|
private ForgeVersionList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||||
|
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
|
||||||
|
final List<Task> dependents = Collections.singletonList(task);
|
||||||
|
return new Task() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
|
||||||
|
if (root == null)
|
||||||
|
return;
|
||||||
|
versions.clear();
|
||||||
|
|
||||||
|
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
||||||
|
String gameVersion = VersionNumber.parseVersion(entry.getKey());
|
||||||
|
if (gameVersion == null)
|
||||||
|
continue;
|
||||||
|
for (int v : entry.getValue()) {
|
||||||
|
ForgeVersion version = root.getNumber().get(v);
|
||||||
|
if (version == null)
|
||||||
|
continue;
|
||||||
|
String jar = null;
|
||||||
|
for (String[] file : version.getFiles())
|
||||||
|
if (file.length > 1 && "installer".equals(file[1])) {
|
||||||
|
String classifier = version.getGameVersion() + "-" + version.getVersion()
|
||||||
|
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
||||||
|
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
|
||||||
|
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jar == null)
|
||||||
|
continue;
|
||||||
|
versions.put(gameVersion, new RemoteVersion<>(
|
||||||
|
version.getGameVersion(), version.getVersion(), jar, null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String FORGE_LIST = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
import org.jackhuang.hmcl.util.Validation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class ForgeVersionRoot implements Validation {
|
||||||
|
|
||||||
|
private final String artifact;
|
||||||
|
private final String webpath;
|
||||||
|
private final String adfly;
|
||||||
|
private final String homepage;
|
||||||
|
private final String name;
|
||||||
|
private final Map<String, int[]> branches;
|
||||||
|
private final Map<String, int[]> mcversion;
|
||||||
|
private final Map<String, Integer> promos;
|
||||||
|
private final Map<Integer, ForgeVersion> number;
|
||||||
|
|
||||||
|
public ForgeVersionRoot() {
|
||||||
|
this(null, null, null, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForgeVersionRoot(String artifact, String webpath, String adfly, String homepage, String name, Map<String, int[]> branches, Map<String, int[]> mcversion, Map<String, Integer> promos, Map<Integer, ForgeVersion> number) {
|
||||||
|
this.artifact = artifact;
|
||||||
|
this.webpath = webpath;
|
||||||
|
this.adfly = adfly;
|
||||||
|
this.homepage = homepage;
|
||||||
|
this.name = name;
|
||||||
|
this.branches = branches;
|
||||||
|
this.mcversion = mcversion;
|
||||||
|
this.promos = promos;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArtifact() {
|
||||||
|
return artifact;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWebPath() {
|
||||||
|
return webpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdfly() {
|
||||||
|
return adfly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHomePage() {
|
||||||
|
return homepage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, int[]> getBranches() {
|
||||||
|
return branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, int[]> getGameVersions() {
|
||||||
|
return mcversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Integer> getPromos() {
|
||||||
|
return promos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, ForgeVersion> getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException {
|
||||||
|
if (number == null)
|
||||||
|
throw new JsonParseException("ForgeVersionRoot number cannot be null");
|
||||||
|
if (mcversion == null)
|
||||||
|
throw new JsonParseException("ForgeVersionRoot mcversion cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class Install {
|
||||||
|
|
||||||
|
private final String profileName;
|
||||||
|
private final String target;
|
||||||
|
private final String path;
|
||||||
|
private final String version;
|
||||||
|
private final String filePath;
|
||||||
|
private final String welcome;
|
||||||
|
private final String minecraft;
|
||||||
|
private final String mirrorList;
|
||||||
|
private final String logo;
|
||||||
|
|
||||||
|
public Install() {
|
||||||
|
this(null, null, null, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Install(String profileName, String target, String path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
|
||||||
|
this.profileName = profileName;
|
||||||
|
this.target = target;
|
||||||
|
this.path = path;
|
||||||
|
this.version = version;
|
||||||
|
this.filePath = filePath;
|
||||||
|
this.welcome = welcome;
|
||||||
|
this.minecraft = minecraft;
|
||||||
|
this.mirrorList = mirrorList;
|
||||||
|
this.logo = logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProfileName() {
|
||||||
|
return profileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWelcome() {
|
||||||
|
return welcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMinecraft() {
|
||||||
|
return minecraft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMirrorList() {
|
||||||
|
return mirrorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogo() {
|
||||||
|
return logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.forge;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
import org.jackhuang.hmcl.util.Validation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class InstallProfile implements Validation {
|
||||||
|
|
||||||
|
@SerializedName("install")
|
||||||
|
private final Install install;
|
||||||
|
|
||||||
|
@SerializedName("versionInfo")
|
||||||
|
private final Version versionInfo;
|
||||||
|
|
||||||
|
public InstallProfile(Install install, Version versionInfo) {
|
||||||
|
this.install = install;
|
||||||
|
this.versionInfo = versionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Install getInstall() {
|
||||||
|
return install;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version getVersionInfo() {
|
||||||
|
return versionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException {
|
||||||
|
if (install == null)
|
||||||
|
throw new JsonParseException("InstallProfile install cannot be null");
|
||||||
|
|
||||||
|
if (versionInfo == null)
|
||||||
|
throw new JsonParseException("InstallProfile versionInfo cannot be null");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||||
|
import org.jackhuang.hmcl.game.AssetObject;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.DigestUtils;
|
||||||
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameAssetDownloadTask extends Task {
|
||||||
|
|
||||||
|
private final AbstractDependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final GameAssetRefreshTask refreshTask;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
|
||||||
|
* @param version the <b>resolved</b> version
|
||||||
|
*/
|
||||||
|
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
this.refreshTask = new GameAssetRefreshTask(dependencyManager, version);
|
||||||
|
this.dependents.add(refreshTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
int size = refreshTask.getResult().size();
|
||||||
|
for (Map.Entry<File, AssetObject> entry : refreshTask.getResult()) {
|
||||||
|
File file = entry.getKey();
|
||||||
|
AssetObject assetObject = entry.getValue();
|
||||||
|
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
|
||||||
|
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) {
|
||||||
|
Logging.LOG.log(Level.SEVERE, "Unable to create new file {0}, because parent directory cannot be created", file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (file.isDirectory())
|
||||||
|
continue;
|
||||||
|
boolean flag = true;
|
||||||
|
int downloaded = 0;
|
||||||
|
try {
|
||||||
|
// check the checksum of file to ensure that the file is not need to re-download.
|
||||||
|
if (file.exists()) {
|
||||||
|
String sha1 = DigestUtils.sha1Hex(FileUtils.readBytes(file));
|
||||||
|
if (sha1.equals(assetObject.getHash())) {
|
||||||
|
++downloaded;
|
||||||
|
Logging.LOG.finest("File $file has been downloaded successfully, skipped downloading");
|
||||||
|
updateProgress(downloaded, size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logging.LOG.log(Level.WARNING, "Unable to get hash code of file " + file, e);
|
||||||
|
flag = !file.exists();
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), assetObject.getHash());
|
||||||
|
task.setName(assetObject.getHash());
|
||||||
|
dependencies.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to download asset index file provided in minecraft.json.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameAssetIndexDownloadTask extends Task {
|
||||||
|
|
||||||
|
private final AbstractDependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
|
||||||
|
* @param version the <b>resolved</b> version
|
||||||
|
*/
|
||||||
|
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
AssetIndexInfo assetIndexInfo = version.getAssetIndex();
|
||||||
|
File assetDir = dependencyManager.getGameRepository().getAssetDirectory(version.getId(), assetIndexInfo.getId());
|
||||||
|
if (FileUtils.makeDirectory(assetDir))
|
||||||
|
throw new IOException("Cannot create directory: " + assetDir);
|
||||||
|
File assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
|
||||||
|
dependencies.add(new FileDownloadTask(
|
||||||
|
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(assetIndexInfo.getUrl())),
|
||||||
|
assetIndexFile, dependencyManager.getProxy()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.AssetIndex;
|
||||||
|
import org.jackhuang.hmcl.game.AssetIndexInfo;
|
||||||
|
import org.jackhuang.hmcl.game.AssetObject;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to extract all asset objects described in asset index json.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File, AssetObject>>> {
|
||||||
|
|
||||||
|
private final AbstractDependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final AssetIndexInfo assetIndexInfo;
|
||||||
|
private final File assetIndexFile;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
|
||||||
|
* @param version the <b>resolved</b> version
|
||||||
|
*/
|
||||||
|
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
this.assetIndexInfo = version.getAssetIndex();
|
||||||
|
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
|
||||||
|
|
||||||
|
if (!assetIndexFile.exists())
|
||||||
|
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, version));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
AssetIndex index = Constants.GSON.fromJson(FileUtils.readText(assetIndexFile), AssetIndex.class);
|
||||||
|
List<Pair<File, AssetObject>> res = new LinkedList<>();
|
||||||
|
int progress = 0;
|
||||||
|
if (index != null)
|
||||||
|
for (AssetObject assetObject : index.getObjects().values()) {
|
||||||
|
res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
|
||||||
|
updateProgress(++progress, index.getObjects().size());
|
||||||
|
}
|
||||||
|
setResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String ID = "game_asset_refresh_task";
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameDownloadTask extends Task {
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
public GameDownloadTask(DefaultDependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
File jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||||
|
|
||||||
|
dependencies.add(new FileDownloadTask(
|
||||||
|
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||||
|
jar,
|
||||||
|
dependencyManager.getProxy(),
|
||||||
|
version.getDownloadInfo().getSha1()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to download game libraries.
|
||||||
|
* This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task).
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameLibrariesTask extends Task {
|
||||||
|
|
||||||
|
private final AbstractDependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
|
||||||
|
* @param version the <b>resolved</b> version
|
||||||
|
*/
|
||||||
|
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
for (Library library : version.getLibraries())
|
||||||
|
if (library.appliesToCurrentEnvironment()) {
|
||||||
|
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
|
||||||
|
if (!file.exists())
|
||||||
|
dependencies.add(new FileDownloadTask(
|
||||||
|
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
|
||||||
|
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.DependencyManager;
|
||||||
|
import org.jackhuang.hmcl.game.DownloadType;
|
||||||
|
import org.jackhuang.hmcl.game.LoggingInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to download log4j configuration file provided in minecraft.json.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameLoggingDownloadTask extends Task {
|
||||||
|
|
||||||
|
private final DependencyManager dependencyManager;
|
||||||
|
private final Version version;
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
|
||||||
|
* @param version the <b>resolved</b> version
|
||||||
|
*/
|
||||||
|
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
if (version.getLogging() == null || !version.getLogging().containsKey(DownloadType.CLIENT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LoggingInfo logging = version.getLogging().get(DownloadType.CLIENT);
|
||||||
|
File file = dependencyManager.getGameRepository().getLoggingObject(version.getId(), version.getAssetIndex().getId(), logging);
|
||||||
|
if (!file.exists())
|
||||||
|
dependencies.add(new FileDownloadTask(NetworkUtils.toURL(logging.getFile().getUrl()), file, dependencyManager.getProxy()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class GameRemoteLatestVersions {
|
||||||
|
|
||||||
|
@SerializedName("snapshot")
|
||||||
|
private final String snapshot;
|
||||||
|
|
||||||
|
@SerializedName("release")
|
||||||
|
private final String release;
|
||||||
|
|
||||||
|
public GameRemoteLatestVersions() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRemoteLatestVersions(String snapshot, String release) {
|
||||||
|
this.snapshot = snapshot;
|
||||||
|
this.release = release;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelease() {
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnapshot() {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.jackhuang.hmcl.game.ReleaseType;
|
||||||
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.Validation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameRemoteVersion implements Validation {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
private final String gameVersion;
|
||||||
|
|
||||||
|
@SerializedName("time")
|
||||||
|
private final Date time;
|
||||||
|
|
||||||
|
@SerializedName("releaseTime")
|
||||||
|
private final Date releaseTime;
|
||||||
|
|
||||||
|
@SerializedName("type")
|
||||||
|
private final ReleaseType type;
|
||||||
|
|
||||||
|
@SerializedName("url")
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public GameRemoteVersion() {
|
||||||
|
this("", new Date(), new Date(), ReleaseType.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type) {
|
||||||
|
this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRemoteVersion(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) {
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.time = time;
|
||||||
|
this.releaseTime = releaseTime;
|
||||||
|
this.type = type;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getReleaseTime() {
|
||||||
|
return releaseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReleaseType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws JsonParseException {
|
||||||
|
if (StringUtils.isBlank(gameVersion))
|
||||||
|
throw new JsonParseException("GameRemoteVersion id cannot be blank");
|
||||||
|
if (StringUtils.isBlank(url))
|
||||||
|
throw new JsonParseException("GameRemoteVersion url cannot be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,27 +15,36 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.game
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import java.util.Date;
|
||||||
import org.jackhuang.hmcl.util.Immutable
|
import org.jackhuang.hmcl.game.ReleaseType;
|
||||||
import org.jackhuang.hmcl.util.Validation
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
class LoggingInfo @JvmOverloads constructor(
|
public final class GameRemoteVersionTag {
|
||||||
@SerializedName("file")
|
|
||||||
val file: IdDownloadInfo = IdDownloadInfo(),
|
|
||||||
|
|
||||||
@SerializedName("argument")
|
private final ReleaseType type;
|
||||||
val argument: String = "",
|
private final Date time;
|
||||||
|
|
||||||
@SerializedName("type")
|
public GameRemoteVersionTag() {
|
||||||
val type: String = ""
|
this(ReleaseType.UNKNOWN, new Date());
|
||||||
): Validation {
|
|
||||||
|
|
||||||
override fun validate() {
|
|
||||||
file.validate()
|
|
||||||
require(argument.isNotBlank(), { "LoggingInfo" })
|
|
||||||
require(type.isNotBlank())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public GameRemoteVersionTag(ReleaseType type, Date time) {
|
||||||
|
this.type = type;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReleaseType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class GameRemoteVersions {
|
||||||
|
|
||||||
|
@SerializedName("versions")
|
||||||
|
private final List<GameRemoteVersion> versions;
|
||||||
|
|
||||||
|
@SerializedName("latest")
|
||||||
|
private final GameRemoteLatestVersions latest;
|
||||||
|
|
||||||
|
public GameRemoteVersions() {
|
||||||
|
this(Collections.EMPTY_LIST, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRemoteVersions(List<GameRemoteVersion> versions, GameRemoteLatestVersions latest) {
|
||||||
|
this.versions = versions;
|
||||||
|
this.latest = latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRemoteLatestVersions getLatest() {
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GameRemoteVersion> getVersions() {
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
|
||||||
|
|
||||||
|
public static final GameVersionList INSTANCE = new GameVersionList();
|
||||||
|
|
||||||
|
private GameVersionList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||||
|
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
|
||||||
|
return new Task() {
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return Collections.singleton(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
versions.clear();
|
||||||
|
|
||||||
|
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
|
||||||
|
for (GameRemoteVersion remoteVersion : root.getVersions()) {
|
||||||
|
String gameVersion = VersionNumber.parseVersion(remoteVersion.getGameVersion());
|
||||||
|
if (gameVersion == null)
|
||||||
|
continue;
|
||||||
|
versions.put(gameVersion, new RemoteVersion<>(
|
||||||
|
remoteVersion.getGameVersion(),
|
||||||
|
remoteVersion.getGameVersion(),
|
||||||
|
remoteVersion.getUrl(),
|
||||||
|
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
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.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class VersionJsonDownloadTask extends Task {
|
||||||
|
private final String gameVersion;
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
private final VersionList<?> gameVersionList;
|
||||||
|
|
||||||
|
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.gameVersionList = dependencyManager.getVersionList("game");
|
||||||
|
|
||||||
|
if (!gameVersionList.isLoaded())
|
||||||
|
dependents.add(gameVersionList.refreshAsync(dependencyManager.getDownloadProvider()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
RemoteVersion<?> remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalStateException("Cannot find specific version "+gameVersion+" in remote repository"));
|
||||||
|
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
|
||||||
|
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), Proxy.NO_PROXY, ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String ID = "raw_version_json";
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.game;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.util.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This task is to save the version json.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class VersionJsonSaveTask extends Task {
|
||||||
|
|
||||||
|
private final DefaultGameRepository repository;
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param repository the game repository
|
||||||
|
* @param version the **resolved** version
|
||||||
|
*/
|
||||||
|
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
File json = repository.getVersionJson(version.getId()).getAbsoluteFile();
|
||||||
|
if (!FileUtils.makeFile(json))
|
||||||
|
throw new IOException("Cannot create file " + json);
|
||||||
|
FileUtils.writeText(json, Constants.GSON.toJson(version));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderBranch {
|
||||||
|
|
||||||
|
@SerializedName("libraries")
|
||||||
|
private final Collection<Library> libraries;
|
||||||
|
|
||||||
|
@SerializedName("com.mumfrey:liteloader")
|
||||||
|
private final Map<String, LiteLoaderVersion> liteLoader;
|
||||||
|
|
||||||
|
public LiteLoaderBranch() {
|
||||||
|
this(Collections.EMPTY_SET, Collections.EMPTY_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderBranch(Collection<Library> libraries, Map<String, LiteLoaderVersion> liteLoader) {
|
||||||
|
this.libraries = libraries;
|
||||||
|
this.liteLoader = liteLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Library> getLibraries() {
|
||||||
|
return Collections.unmodifiableCollection(libraries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, LiteLoaderVersion> getLiteLoader() {
|
||||||
|
return Collections.unmodifiableMap(liteLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderGameVersions {
|
||||||
|
|
||||||
|
@SerializedName("repo")
|
||||||
|
private final LiteLoaderRepository repoitory;
|
||||||
|
|
||||||
|
@SerializedName("artefacts")
|
||||||
|
private final LiteLoaderBranch artifacts;
|
||||||
|
|
||||||
|
@SerializedName("snapshots")
|
||||||
|
private final LiteLoaderBranch snapshots;
|
||||||
|
|
||||||
|
public LiteLoaderGameVersions() {
|
||||||
|
this(null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderGameVersions(LiteLoaderRepository repoitory, LiteLoaderBranch artifacts, LiteLoaderBranch snapshots) {
|
||||||
|
this.repoitory = repoitory;
|
||||||
|
this.artifacts = artifacts;
|
||||||
|
this.snapshots = snapshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderRepository getRepoitory() {
|
||||||
|
return repoitory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderBranch getArtifacts() {
|
||||||
|
return artifacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderBranch getSnapshots() {
|
||||||
|
return snapshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||||
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: LiteLoader must be installed after Forge.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class LiteLoaderInstallTask extends TaskResult<Version> {
|
||||||
|
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final String gameVersion;
|
||||||
|
private final Version version;
|
||||||
|
private final String remoteVersion;
|
||||||
|
private final LiteLoaderVersionList liteLoaderVersionList;
|
||||||
|
private RemoteVersion<LiteLoaderRemoteVersionTag> remote;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
private void doRemote() {
|
||||||
|
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Remote LiteLoader version " + gameVersion + ", " + remoteVersion + " not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.version = version;
|
||||||
|
this.remoteVersion = remoteVersion;
|
||||||
|
|
||||||
|
liteLoaderVersionList = (LiteLoaderVersionList) dependencyManager.getVersionList("liteloader");
|
||||||
|
|
||||||
|
if (!liteLoaderVersionList.isLoaded())
|
||||||
|
dependents.add(liteLoaderVersionList.refreshAsync(dependencyManager.getDownloadProvider())
|
||||||
|
.then(s -> {
|
||||||
|
doRemote();
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
doRemote();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "version";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
Library library = new Library(
|
||||||
|
"com.mumfrey", "liteloader", remote.getSelfVersion(), null,
|
||||||
|
"http://dl.liteloader.com/versions/",
|
||||||
|
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||||
|
);
|
||||||
|
|
||||||
|
Version tempVersion = version.setLibraries(Lang.merge(remote.getTag().getLibraries(), Arrays.asList(library)));
|
||||||
|
setResult(version
|
||||||
|
.setMainClass("net.minecraft.launchwrapper.Launch")
|
||||||
|
.setLibraries(Lang.merge(tempVersion.getLibraries(), version.getLibraries()))
|
||||||
|
.setLogging(Collections.EMPTY_MAP)
|
||||||
|
.setMinecraftArguments(version.getMinecraftArguments().orElse("") + " --tweakClass " + remote.getTag().getTweakClass())
|
||||||
|
//.setArguments(Arguments.addGameArguments(Lang.get(version.getArguments()), "--tweakClass", remote.getTag().getTweakClass()))
|
||||||
|
);
|
||||||
|
|
||||||
|
dependencies.add(new GameLibrariesTask(dependencyManager, tempVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class LiteLoaderRemoteVersionTag {
|
||||||
|
private final String tweakClass;
|
||||||
|
private final Collection<Library> libraries;
|
||||||
|
|
||||||
|
public LiteLoaderRemoteVersionTag() {
|
||||||
|
this("", Collections.EMPTY_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderRemoteVersionTag(String tweakClass, Collection<Library> libraries) {
|
||||||
|
this.tweakClass = tweakClass;
|
||||||
|
this.libraries = libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Library> getLibraries() {
|
||||||
|
return libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTweakClass() {
|
||||||
|
return tweakClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderRepository {
|
||||||
|
|
||||||
|
@SerializedName("stream")
|
||||||
|
private final String stream;
|
||||||
|
|
||||||
|
@SerializedName("type")
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
@SerializedName("url")
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
@SerializedName("classifier")
|
||||||
|
private final String classifier;
|
||||||
|
|
||||||
|
public LiteLoaderRepository() {
|
||||||
|
this("", "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderRepository(String stream, String type, String url, String classifier) {
|
||||||
|
this.stream = stream;
|
||||||
|
this.type = type;
|
||||||
|
this.url = url;
|
||||||
|
this.classifier = classifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStream() {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassifier() {
|
||||||
|
return classifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderVersion {
|
||||||
|
private final String tweakClass;
|
||||||
|
private final String file;
|
||||||
|
private final String version;
|
||||||
|
private final String md5;
|
||||||
|
private final String timestamp;
|
||||||
|
private final int lastSuccessfulBuild;
|
||||||
|
private final Collection<Library> libraries;
|
||||||
|
|
||||||
|
public LiteLoaderVersion() {
|
||||||
|
this("", "", "", "", "", 0, Collections.EMPTY_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderVersion(String tweakClass, String file, String version, String md5, String timestamp, int lastSuccessfulBuild, Collection<Library> libraries) {
|
||||||
|
this.tweakClass = tweakClass;
|
||||||
|
this.file = file;
|
||||||
|
this.version = version;
|
||||||
|
this.md5 = md5;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.lastSuccessfulBuild = lastSuccessfulBuild;
|
||||||
|
this.libraries = libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTweakClass() {
|
||||||
|
return tweakClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMd5() {
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastSuccessfulBuild() {
|
||||||
|
return lastSuccessfulBuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Library> getLibraries() {
|
||||||
|
return Collections.unmodifiableCollection(libraries);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
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.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersionTag> {
|
||||||
|
|
||||||
|
public static final LiteLoaderVersionList INSTANCE = new LiteLoaderVersionList();
|
||||||
|
|
||||||
|
private LiteLoaderVersionList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||||
|
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
|
||||||
|
return new Task() {
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return Collections.singleton(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
|
||||||
|
versions.clear();
|
||||||
|
|
||||||
|
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||||
|
String gameVersion = entry.getKey();
|
||||||
|
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||||
|
String gg = VersionNumber.parseVersion(gameVersion);
|
||||||
|
if (gg == null)
|
||||||
|
continue;
|
||||||
|
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||||
|
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||||
|
if (branch == null || repository == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||||
|
String branchName = entry.getKey();
|
||||||
|
LiteLoaderVersion v = entry.getValue();
|
||||||
|
if ("latest".equals(branchName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
versions.put(key, new RemoteVersion<>(gameVersion,
|
||||||
|
v.getVersion().replace("SNAPSHOT", "SNAPSHOT-" + v.getLastSuccessfulBuild()),
|
||||||
|
snapshot
|
||||||
|
? "http://jenkins.liteloader.com/LiteLoader " + gameVersion + "/lastSuccessfulBuild/artifact/build/libs/liteloader-" + v.getVersion() + "-release.jar"
|
||||||
|
: downloadProvider.injectURL(repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile()),
|
||||||
|
new LiteLoaderRemoteVersionTag(v.getTweakClass(), v.getLibraries())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderVersionsMeta {
|
||||||
|
|
||||||
|
@SerializedName("description")
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
@SerializedName("authors")
|
||||||
|
private final String authors;
|
||||||
|
|
||||||
|
@SerializedName("url")
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public LiteLoaderVersionsMeta() {
|
||||||
|
this("", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderVersionsMeta(String description, String authors, String url) {
|
||||||
|
this.description = description;
|
||||||
|
this.authors = authors;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthors() {
|
||||||
|
return authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public final class LiteLoaderVersionsRoot {
|
||||||
|
|
||||||
|
@SerializedName("versions")
|
||||||
|
private final Map<String, LiteLoaderGameVersions> versions;
|
||||||
|
|
||||||
|
@SerializedName("meta")
|
||||||
|
private final LiteLoaderVersionsMeta meta;
|
||||||
|
|
||||||
|
public LiteLoaderVersionsRoot() {
|
||||||
|
this(Collections.EMPTY_MAP, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderVersionsRoot(Map<String, LiteLoaderGameVersions> versions, LiteLoaderVersionsMeta meta) {
|
||||||
|
this.versions = versions;
|
||||||
|
this.meta = meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, LiteLoaderGameVersions> getVersions() {
|
||||||
|
return Collections.unmodifiableMap(versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteLoaderVersionsMeta getMeta() {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
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.Constants;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jackhuang.hmcl.util.VersionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFineBMCLVersionList extends VersionList<Void> {
|
||||||
|
|
||||||
|
public static final OptiFineBMCLVersionList INSTANCE = new OptiFineBMCLVersionList();
|
||||||
|
|
||||||
|
private OptiFineBMCLVersionList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||||
|
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi.bangbang93.com/optifine/versionlist"));
|
||||||
|
return new Task() {
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return Collections.singleton(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
versions.clear();
|
||||||
|
Set<String> duplicates = new HashSet<>();
|
||||||
|
List<OptiFineVersion> root = Constants.GSON.fromJson(task.getResult(), new TypeToken<List<OptiFineVersion>>() {
|
||||||
|
}.getType());
|
||||||
|
for (OptiFineVersion element : root) {
|
||||||
|
String version = element.getType();
|
||||||
|
if (version == null)
|
||||||
|
continue;
|
||||||
|
String mirror = "http://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
||||||
|
if (!duplicates.add(mirror))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(element.getGameVersion()))
|
||||||
|
continue;
|
||||||
|
String gameVersion = VersionNumber.parseVersion(element.getGameVersion());
|
||||||
|
versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, mirror, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFineDownloadFormatter extends TaskResult<String> {
|
||||||
|
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public OptiFineDownloadFormatter(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
String result = null;
|
||||||
|
String content = NetworkUtils.doGet(NetworkUtils.toURL(url));
|
||||||
|
Matcher m = PATTERN.matcher(content);
|
||||||
|
while (m.find())
|
||||||
|
result = m.group(1);
|
||||||
|
if (result == null)
|
||||||
|
throw new IllegalStateException("Cannot find version in " + content);
|
||||||
|
setResult("http://optifine.net/downloadx?f=OptiFine" + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String ID = "optifine_formatter";
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("\"downloadx\\?f=OptiFine(.*)\"");
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.VersionList;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||||
|
import org.jackhuang.hmcl.game.Argument;
|
||||||
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||||
|
import org.jackhuang.hmcl.game.Library;
|
||||||
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||||
|
import org.jackhuang.hmcl.game.StringArgument;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Note</b>: OptiFine should be installed in the end.
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFineInstallTask extends TaskResult<Version> {
|
||||||
|
|
||||||
|
private final DefaultDependencyManager dependencyManager;
|
||||||
|
private final String gameVersion;
|
||||||
|
private final Version version;
|
||||||
|
private final String remoteVersion;
|
||||||
|
private final VersionList<?> optiFineVersionList;
|
||||||
|
private RemoteVersion<?> remote;
|
||||||
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
|
private final List<Task> dependencies = new LinkedList<>();
|
||||||
|
|
||||||
|
private void doRemote() {
|
||||||
|
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Remote OptiFine version " + gameVersion + ", " + remoteVersion + " not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
|
||||||
|
this.dependencyManager = dependencyManager;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.version = version;
|
||||||
|
this.remoteVersion = remoteVersion;
|
||||||
|
|
||||||
|
optiFineVersionList = dependencyManager.getVersionList("optifine");
|
||||||
|
|
||||||
|
if (!optiFineVersionList.isLoaded())
|
||||||
|
dependents.add(optiFineVersionList.refreshAsync(dependencyManager.getDownloadProvider())
|
||||||
|
.then(s -> {
|
||||||
|
doRemote();
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
else
|
||||||
|
doRemote();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "version";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRelyingOnDependencies() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
Library library = new Library(
|
||||||
|
"net.optifine", "optifine", remoteVersion, null, null,
|
||||||
|
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
||||||
|
"net/optifine/optifine/" + remoteVersion + "/optifine-" + remoteVersion + ".jar",
|
||||||
|
remote.getUrl())), true
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Library> libraries = new LinkedList<>();
|
||||||
|
libraries.add(library);
|
||||||
|
|
||||||
|
boolean hasFMLTweaker = false;
|
||||||
|
if (version.getMinecraftArguments().isPresent() && version.getMinecraftArguments().get().contains("FMLTweaker"))
|
||||||
|
hasFMLTweaker = true;
|
||||||
|
if (version.getArguments().isPresent()) {
|
||||||
|
List<Argument> game = version.getArguments().get().getGame();
|
||||||
|
if (game.stream().anyMatch(arg -> arg.toString(Collections.EMPTY_MAP, Collections.EMPTY_MAP).contains("FMLTweaker")))
|
||||||
|
hasFMLTweaker = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Arguments arguments = Lang.get(version.getArguments());
|
||||||
|
|
||||||
|
if (!hasFMLTweaker)
|
||||||
|
arguments = Arguments.addGameArguments(arguments, "--tweakClass", "optifine.OptiFineTweaker");
|
||||||
|
*/
|
||||||
|
String minecraftArguments = version.getMinecraftArguments().orElse("");
|
||||||
|
if (!hasFMLTweaker)
|
||||||
|
minecraftArguments = minecraftArguments + " --tweakClass optifine.OptiFineTweaker";
|
||||||
|
|
||||||
|
if (version.getMainClass() == null || !version.getMainClass().startsWith("net.minecraft.launchwrapper."))
|
||||||
|
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
|
||||||
|
|
||||||
|
setResult(version
|
||||||
|
.setLibraries(Lang.merge(version.getLibraries(), libraries))
|
||||||
|
.setMainClass("net.minecraft.launchwrapper.Launch")
|
||||||
|
.setMinecraftArguments(minecraftArguments)
|
||||||
|
//.setArguments(arguments)
|
||||||
|
);
|
||||||
|
|
||||||
|
dependencies.add(new GameLibrariesTask(dependencyManager, version.setLibraries(libraries)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFineVersion {
|
||||||
|
|
||||||
|
@SerializedName("dl")
|
||||||
|
private final String downloadLink;
|
||||||
|
|
||||||
|
@SerializedName("ver")
|
||||||
|
private final String version;
|
||||||
|
|
||||||
|
@SerializedName("date")
|
||||||
|
private final String date;
|
||||||
|
|
||||||
|
@SerializedName("type")
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
@SerializedName("patch")
|
||||||
|
private final String patch;
|
||||||
|
|
||||||
|
@SerializedName("mirror")
|
||||||
|
private final String mirror;
|
||||||
|
|
||||||
|
@SerializedName("mcversion")
|
||||||
|
private final String gameVersion;
|
||||||
|
|
||||||
|
public OptiFineVersion() {
|
||||||
|
this(null, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptiFineVersion(String downloadLink, String version, String date, String type, String patch, String mirror, String gameVersion) {
|
||||||
|
this.downloadLink = downloadLink;
|
||||||
|
this.version = version;
|
||||||
|
this.date = date;
|
||||||
|
this.type = type;
|
||||||
|
this.patch = patch;
|
||||||
|
this.mirror = mirror;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDownloadLink() {
|
||||||
|
return downloadLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPatch() {
|
||||||
|
return patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMirror() {
|
||||||
|
return mirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.download.optifine;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
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.NetworkUtils;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFineVersionList extends VersionList<Void> {
|
||||||
|
|
||||||
|
private static final Pattern PATTERN = Pattern.compile("OptiFine (.*?) ");
|
||||||
|
public static final OptiFineVersionList INSTANCE = new OptiFineVersionList();
|
||||||
|
|
||||||
|
private OptiFineVersionList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task refreshAsync(DownloadProvider downloadProvider) {
|
||||||
|
GetTask task = new GetTask(NetworkUtils.toURL("http://optifine.net/downloads"));
|
||||||
|
return new Task() {
|
||||||
|
@Override
|
||||||
|
public Collection<Task> getDependents() {
|
||||||
|
return Collections.singleton(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
versions.clear();
|
||||||
|
|
||||||
|
String html = task.getResult().replace(" ", " ").replace(">", ">").replace("<", "<").replace("<br>", "<br />");
|
||||||
|
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder db = factory.newDocumentBuilder();
|
||||||
|
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
|
||||||
|
Element r = doc.getDocumentElement();
|
||||||
|
NodeList tables = r.getElementsByTagName("table");
|
||||||
|
for (int i = 0; i < tables.getLength(); i++) {
|
||||||
|
Element e = (Element) tables.item(i);
|
||||||
|
if ("downloadTable".equals(e.getAttribute("class"))) {
|
||||||
|
NodeList tr = e.getElementsByTagName("tr");
|
||||||
|
for (int k = 0; k < tr.getLength(); k++) {
|
||||||
|
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
|
||||||
|
String url = null, version = null, gameVersion = null;
|
||||||
|
for (int j = 0; j < downloadLine.getLength(); j++) {
|
||||||
|
Element td = (Element) downloadLine.item(j);
|
||||||
|
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
|
||||||
|
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
|
||||||
|
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
|
||||||
|
version = td.getTextContent();
|
||||||
|
}
|
||||||
|
Matcher matcher = PATTERN.matcher(version);
|
||||||
|
while (matcher.find())
|
||||||
|
gameVersion = matcher.group(1);
|
||||||
|
if (gameVersion == null || version == null || url == null)
|
||||||
|
continue;
|
||||||
|
versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, url, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
HMCLCore/src/main/java/org/jackhuang/hmcl/event/Event.java
Normal file
85
HMCLCore/src/main/java/org/jackhuang/hmcl/event/Event.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public class Event extends EventObject {
|
||||||
|
|
||||||
|
public Event(Object source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canceled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if this event is canceled.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
|
||||||
|
*/
|
||||||
|
public final boolean isCanceled() {
|
||||||
|
return canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param canceled new value
|
||||||
|
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
|
||||||
|
*/
|
||||||
|
public final void setCanceled(boolean canceled) {
|
||||||
|
if (!isCancelable())
|
||||||
|
throw new UnsupportedOperationException("Attempted to cancel a non-cancelable event: " + getClass());
|
||||||
|
this.canceled = canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if this Event this cancelable.
|
||||||
|
*/
|
||||||
|
public boolean isCancelable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasResult() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retutns the value set as the result of this event
|
||||||
|
*/
|
||||||
|
public Result getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(Result result) {
|
||||||
|
if (!hasResult())
|
||||||
|
throw new UnsupportedOperationException("Attempted to set result on a no result event: " + this.getClass() + " of type.");
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Result {
|
||||||
|
DENY,
|
||||||
|
DEFAULT,
|
||||||
|
ALLOW
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class EventBus {
|
||||||
|
|
||||||
|
private final HashMap<Class<?>, EventManager<?>> events = new HashMap<>();
|
||||||
|
|
||||||
|
public <T extends EventObject> EventManager<T> channel(Class<T> clazz) {
|
||||||
|
if (!events.containsKey(clazz))
|
||||||
|
events.put(clazz, new EventManager<>(Schedulers.computation()));
|
||||||
|
return (EventManager<T>) events.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireEvent(EventObject obj) {
|
||||||
|
channel((Class<EventObject>) obj.getClass()).fireEvent(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final EventBus EVENT_BUS = new EventBus();
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.event;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.EventObject;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.jackhuang.hmcl.task.Scheduler;
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class EventManager<T extends EventObject> {
|
||||||
|
|
||||||
|
private final Scheduler scheduler;
|
||||||
|
private final SimpleMultimap<EventPriority, Consumer<T>> handlers
|
||||||
|
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
|
||||||
|
private final SimpleMultimap<EventPriority, Runnable> handlers2
|
||||||
|
= new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new);
|
||||||
|
|
||||||
|
public EventManager() {
|
||||||
|
this(Schedulers.immediate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventManager(Scheduler scheduler) {
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Consumer<T> consumer) {
|
||||||
|
register(consumer, EventPriority.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Consumer<T> consumer, EventPriority priority) {
|
||||||
|
if (!handlers.get(priority).contains(consumer))
|
||||||
|
handlers.put(priority, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Runnable runnable) {
|
||||||
|
register(runnable, EventPriority.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Runnable runnable, EventPriority priority) {
|
||||||
|
if (!handlers2.get(priority).contains(runnable))
|
||||||
|
handlers2.put(priority, runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister(Consumer<T> consumer) {
|
||||||
|
handlers.removeValue(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister(Runnable runnable) {
|
||||||
|
handlers2.removeValue(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireEvent(T event) {
|
||||||
|
scheduler.schedule(() -> {
|
||||||
|
for (EventPriority priority : EventPriority.values()) {
|
||||||
|
for (Consumer<T> handler : handlers.get(priority))
|
||||||
|
handler.accept(event);
|
||||||
|
for (Runnable runnable : handlers2.get(priority))
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,12 +15,16 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.event
|
package org.jackhuang.hmcl.event;
|
||||||
|
|
||||||
enum class EventPriority {
|
/**
|
||||||
|
*
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public enum EventPriority {
|
||||||
HIGHEST,
|
HIGHEST,
|
||||||
HIGH,
|
HIGH,
|
||||||
NORMAL,
|
NORMAL,
|
||||||
LOW,
|
LOW,
|
||||||
LOWEST
|
LOWEST
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
public class FailedEvent<T> extends EventObject {
|
||||||
|
|
||||||
|
private final int failedTime;
|
||||||
|
private T newResult;
|
||||||
|
|
||||||
|
public FailedEvent(Object source, int failedTime, T newResult) {
|
||||||
|
super(source);
|
||||||
|
this.failedTime = failedTime;
|
||||||
|
this.newResult = newResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFailedTime() {
|
||||||
|
return failedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getNewResult() {
|
||||||
|
return newResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNewResult(T newResult) {
|
||||||
|
this.newResult = newResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user