Turn to java
This commit is contained in:
@@ -22,11 +22,13 @@ import javafx.application.Platform
|
||||
import javafx.stage.Stage
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.ui.Controllers
|
||||
import org.jackhuang.hmcl.ui.runOnUiThread
|
||||
import org.jackhuang.hmcl.util.DEFAULT_USER_AGENT
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import org.jackhuang.hmcl.util.OS
|
||||
import org.jackhuang.hmcl.util.Constants
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import org.jackhuang.hmcl.util.NetworkUtils
|
||||
import org.jackhuang.hmcl.util.OperatingSystem
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
|
||||
@@ -34,7 +36,7 @@ fun i18n(key: String): String {
|
||||
try {
|
||||
return Main.RESOURCE_BUNDLE.getString(key)
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -62,20 +64,21 @@ class Main : Application() {
|
||||
|
||||
@JvmStatic
|
||||
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)
|
||||
}
|
||||
|
||||
fun getWorkingDirectory(folder: String): File {
|
||||
val userhome = System.getProperty("user.home", ".")
|
||||
return when (OS.CURRENT_OS) {
|
||||
OS.LINUX -> File(userhome, ".$folder/")
|
||||
OS.WINDOWS -> {
|
||||
return when (OperatingSystem.CURRENT_OS) {
|
||||
OperatingSystem.LINUX -> File(userhome, ".$folder/")
|
||||
OperatingSystem.WINDOWS -> {
|
||||
val appdata: String? = System.getenv("APPDATA")
|
||||
File(appdata ?: userhome, ".$folder/")
|
||||
}
|
||||
OS.OSX -> File(userhome, "Library/Application Support/" + folder)
|
||||
OperatingSystem.OSX -> File(userhome, "Library/Application Support/" + folder)
|
||||
else -> File(userhome, "$folder/")
|
||||
}
|
||||
}
|
||||
@@ -89,7 +92,7 @@ class Main : Application() {
|
||||
|
||||
fun stopWithoutJavaFXPlatform() = runOnUiThread {
|
||||
Controllers.stage.close()
|
||||
Scheduler.shutdown()
|
||||
Schedulers.shutdown()
|
||||
}
|
||||
|
||||
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.task.FileDownloadTask
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import org.jackhuang.hmcl.ui.DEFAULT_ICON
|
||||
import org.jackhuang.hmcl.ui.DialogController
|
||||
@@ -48,11 +49,13 @@ object AccountHelper {
|
||||
SkinLoadTask(account, proxy, true)
|
||||
|
||||
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() {
|
||||
if (account.canLogIn && (account.selectedProfile == null || refresh))
|
||||
if (account.canLogIn() && (account.selectedProfile == null || refresh))
|
||||
DialogController.logIn(account)
|
||||
val profile = account.selectedProfile ?: return
|
||||
val name = profile.name ?: return
|
||||
@@ -60,7 +63,7 @@ object AccountHelper {
|
||||
val file = getSkinFile(name)
|
||||
if (!refresh && file.exists())
|
||||
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.Settings
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import org.jackhuang.hmcl.util.fromJson
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -72,7 +72,7 @@ class HMCLGameRepository(val profile: Profile, baseDirectory: File)
|
||||
|
||||
@Synchronized
|
||||
override fun refreshVersionsImpl() {
|
||||
Scheduler.NEW_THREAD.schedule {
|
||||
Schedulers.newThread().schedule {
|
||||
versionSettings.clear()
|
||||
|
||||
super.refreshVersionsImpl()
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
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.setting.Profile
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
@@ -26,6 +27,8 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
import org.jackhuang.hmcl.task.TaskResult
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Constants.GSON
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.util.ArrayList
|
||||
|
||||
/**
|
||||
@@ -38,39 +41,42 @@ import java.util.ArrayList
|
||||
*/
|
||||
@Throws(IOException::class, JsonParseException::class)
|
||||
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 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.")
|
||||
return if (game.jar == null)
|
||||
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
|
||||
else manifest.copy(manifest = HMCLModpackManifest)
|
||||
else manifest.copy(manifest = HMCLModpackManifest, gameVersion = game.jar!!)
|
||||
else manifest.setManifest(HMCLModpackManifest)
|
||||
else manifest.setManifest(HMCLModpackManifest).setGameVersion(game.jar)
|
||||
}
|
||||
|
||||
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 repository = profile.repository
|
||||
override val dependencies = mutableListOf<Task>()
|
||||
override val dependents = mutableListOf<Task>()
|
||||
private val dependencies = mutableListOf<Task>()
|
||||
private val dependents = mutableListOf<Task>()
|
||||
|
||||
override fun getDependencies() = dependencies
|
||||
override fun getDependents() = dependents
|
||||
|
||||
init {
|
||||
check(!repository.hasVersion(name), { "Version $name already exists." })
|
||||
val json = zipFile.readTextZipEntry("minecraft/pack.json")
|
||||
check(!repository.hasVersion(version), { "Version $version already exists." })
|
||||
val json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json")
|
||||
var version = GSON.fromJson<Version>(json)!!
|
||||
version = version.copy(jar = null)
|
||||
dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync()
|
||||
dependencies += VersionJSONSaveTask(repository, version) // override the json created by buildAsync()
|
||||
version = version.setJar(null)
|
||||
dependents += dependency.gameBuilder().name(this.version).gameVersion(modpack.gameVersion!!).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() {
|
||||
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 modpack: Modpack,
|
||||
private val output: File,
|
||||
override val id: String = ID): TaskResult<ZipEngine>() {
|
||||
private val id: String = ID): TaskResult<ZipEngine>() {
|
||||
|
||||
override fun getId() = id
|
||||
|
||||
init {
|
||||
onDone += { event -> if (event.failed) output.delete() }
|
||||
onDone() += { event -> if (event.isFailed) output.delete() }
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
@@ -148,8 +156,8 @@ class HMCLModpackExportTask @JvmOverloads constructor(
|
||||
|
||||
val mv = repository.getVersion(version).resolve(repository)
|
||||
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(modpack.copy(gameVersion = gameVersion)), "modpack.json") // Newer HMCL only reads 'gameVersion' field.
|
||||
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.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.launch.DefaultLauncher
|
||||
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.Settings
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
@@ -31,10 +31,10 @@ import org.jackhuang.hmcl.task.*
|
||||
import org.jackhuang.hmcl.ui.*
|
||||
import org.jackhuang.hmcl.util.Log4jLevel
|
||||
import org.jackhuang.hmcl.util.ManagedProcess
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
|
||||
object LauncherHelper {
|
||||
private val launchingStepsPane = LaunchingStepsPane()
|
||||
val PROCESS = ConcurrentLinkedQueue<ManagedProcess>()
|
||||
@@ -49,13 +49,13 @@ object LauncherHelper {
|
||||
val setting = profile.getVersionSetting(selectedVersion)
|
||||
|
||||
Controllers.dialog(launchingStepsPane)
|
||||
task(Scheduler.JAVAFX) { emitStatus(LoadingState.DEPENDENCIES) }
|
||||
task(Schedulers.javafx()) { emitStatus(LoadingState.DEPENDENCIES) }
|
||||
.then(dependency.checkGameCompletionAsync(version))
|
||||
|
||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.MODS) })
|
||||
.then(CurseForgeModpackCompletionTask(dependency, selectedVersion))
|
||||
.then(task(Schedulers.javafx()) { emitStatus(LoadingState.MODS) })
|
||||
.then(CurseCompletionTask(dependency, selectedVersion))
|
||||
|
||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LOGIN) })
|
||||
.then(task(Schedulers.javafx()) { emitStatus(LoadingState.LOGIN) })
|
||||
.then(task {
|
||||
try {
|
||||
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 {
|
||||
it["launcher"] = HMCLGameLauncher(
|
||||
repository = repository,
|
||||
@@ -84,12 +84,12 @@ object LauncherHelper {
|
||||
|
||||
.executor()
|
||||
.apply {
|
||||
taskListener = object : TaskListener {
|
||||
taskListener = object : TaskListener() {
|
||||
var finished = 0
|
||||
|
||||
override fun onFinished(task: Task) {
|
||||
++finished
|
||||
runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / totTask.get() }
|
||||
runOnUiThread { launchingStepsPane.pgsTasks.progress = 1.0 * finished / runningTasks }
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
@@ -179,9 +179,14 @@ object LauncherHelper {
|
||||
if (exitType != ProcessListener.ExitType.NORMAL && logWindow == null){
|
||||
runOnUiThread {
|
||||
LogWindow().apply {
|
||||
for ((line, level) in logs)
|
||||
logLine(line, level)
|
||||
show()
|
||||
Schedulers.newThread().schedule {
|
||||
waitForShown()
|
||||
runOnUiThread {
|
||||
for ((line, level) in logs)
|
||||
logLine(line, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,18 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import org.jackhuang.hmcl.mod.InstanceConfiguration
|
||||
import org.jackhuang.hmcl.mod.Modpack
|
||||
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
||||
import org.jackhuang.hmcl.mod.readMMCModpackManifest
|
||||
import org.jackhuang.hmcl.mod.*
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
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.util.toStringOrEmpty
|
||||
import java.io.File
|
||||
|
||||
fun readModpackManifest(f: File): Modpack {
|
||||
try {
|
||||
return readCurseForgeModpackManifest(f)
|
||||
return CurseManifest.readCurseForgeModpackManifest(f);
|
||||
} catch (e: Exception) {
|
||||
// ignore it, not a valid CurseForge modpack.
|
||||
}
|
||||
@@ -44,7 +41,7 @@ fun readModpackManifest(f: File): Modpack {
|
||||
}
|
||||
|
||||
try {
|
||||
val manifest = readMMCModpackManifest(f)
|
||||
val manifest = MultiMCInstanceConfiguration.readMultiMCModpackManifest(f)
|
||||
return manifest
|
||||
} catch (e: Exception) {
|
||||
// ignore it, not a valid MMC modpack.
|
||||
@@ -53,36 +50,36 @@ fun readModpackManifest(f: File): Modpack {
|
||||
throw IllegalArgumentException("Modpack file $f is not supported.")
|
||||
}
|
||||
|
||||
fun InstanceConfiguration.toVersionSetting(vs: VersionSetting) {
|
||||
fun MultiMCInstanceConfiguration.toVersionSetting(vs: VersionSetting) {
|
||||
vs.usesGlobal = false
|
||||
vs.gameDirType = EnumGameDirectory.VERSION_FOLDER
|
||||
|
||||
if (overrideJavaLocation) {
|
||||
if (isOverrideJavaLocation) {
|
||||
vs.javaDir = javaPath.toStringOrEmpty()
|
||||
}
|
||||
|
||||
if (overrideMemory) {
|
||||
if (isOverrideMemory) {
|
||||
vs.permSize = permGen.toStringOrEmpty()
|
||||
if (maxMemory != null)
|
||||
vs.maxMemory = maxMemory!!
|
||||
vs.minMemory = minMemory
|
||||
}
|
||||
|
||||
if (overrideCommands) {
|
||||
if (isOverrideCommands) {
|
||||
vs.wrapper = wrapperCommand.orEmpty()
|
||||
vs.precalledCommand = preLaunchCommand.orEmpty()
|
||||
}
|
||||
|
||||
if (overrideJavaArgs) {
|
||||
if (isOverrideJavaArgs) {
|
||||
vs.javaArgs = jvmArgs.orEmpty()
|
||||
}
|
||||
|
||||
if (overrideConsole) {
|
||||
vs.showLogs = showConsole
|
||||
if (isOverrideConsole) {
|
||||
vs.showLogs = isShowConsole
|
||||
}
|
||||
|
||||
if (overrideWindow) {
|
||||
vs.fullscreen = fullscreen
|
||||
if (isOverrideWindow) {
|
||||
vs.fullscreen = isFullscreen
|
||||
if (width != null)
|
||||
vs.width = width!!
|
||||
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() {
|
||||
override val scheduler = Scheduler.JAVAFX
|
||||
class MMCInstallVersionSettingTask(private val profile: Profile, val manifest: MultiMCInstanceConfiguration, private val version: String): Task() {
|
||||
override fun getScheduler() = Schedulers.javafx()
|
||||
override fun execute() {
|
||||
val vs = profile.specializeVersionSetting(name)!!
|
||||
val vs = profile.specializeVersionSetting(version)!!
|
||||
manifest.toVersionSetting(vs)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.setting
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
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.game.HMCLGameRepository
|
||||
import org.jackhuang.hmcl.mod.ModManager
|
||||
@@ -49,7 +49,7 @@ class Profile(name: String = "Default", initialGameDir: File = File(".minecraft"
|
||||
init {
|
||||
gameDirProperty.onChange { repository.changeDirectory(it!!) }
|
||||
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 {
|
||||
|
||||
@@ -27,9 +27,10 @@ import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.event.EventBus
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.Authenticator
|
||||
@@ -43,7 +44,7 @@ object Settings {
|
||||
val GSON = GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
||||
.registerTypeAdapter(Profile::class.java, Profile)
|
||||
.registerTypeAdapter(File::class.java, FileTypeAdapter)
|
||||
.registerTypeAdapter(File::class.java, FileTypeAdapter.INSTANCE)
|
||||
.setPrettyPrinting().create()
|
||||
|
||||
const val DEFAULT_PROFILE = "Default"
|
||||
@@ -209,14 +210,14 @@ object Settings {
|
||||
|
||||
var downloadProvider: DownloadProvider
|
||||
get() = when (SETTINGS.downloadtype) {
|
||||
0 -> MojangDownloadProvider
|
||||
1 -> BMCLAPIDownloadProvider
|
||||
else -> MojangDownloadProvider
|
||||
0 -> MojangDownloadProvider.INSTANCE
|
||||
1 -> BMCLAPIDownloadProvider.INSTANCE
|
||||
else -> MojangDownloadProvider.INSTANCE
|
||||
}
|
||||
set(value) {
|
||||
SETTINGS.downloadtype = when (value) {
|
||||
MojangDownloadProvider -> 0
|
||||
BMCLAPIDownloadProvider -> 1
|
||||
MojangDownloadProvider.INSTANCE -> 0
|
||||
BMCLAPIDownloadProvider.INSTANCE -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -275,14 +276,14 @@ object Settings {
|
||||
get() {
|
||||
if (!hasProfile(SETTINGS.selectedProfile)) {
|
||||
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
||||
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
||||
Schedulers.computation().schedule { onProfileChanged() }
|
||||
}
|
||||
return getProfile(SETTINGS.selectedProfile)
|
||||
}
|
||||
set(value) {
|
||||
if (hasProfile(value.name) && value.name != SETTINGS.selectedProfile) {
|
||||
SETTINGS.selectedProfile = value.name
|
||||
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
||||
Schedulers.computation().schedule { onProfileChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,14 +328,14 @@ object Settings {
|
||||
}
|
||||
val flag = getProfileMap().remove(ver) != null
|
||||
if (flag)
|
||||
Scheduler.COMPUTATION.schedule { onProfileLoading() }
|
||||
Schedulers.computation().schedule { onProfileLoading() }
|
||||
|
||||
return flag
|
||||
}
|
||||
|
||||
internal fun onProfileChanged() {
|
||||
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.
|
||||
*/
|
||||
fun onProfileLoading() {
|
||||
EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
||||
EventBus.EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
||||
onProfileChanged()
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import javafx.scene.text.Font
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.AccountFactory
|
||||
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.YggdrasilAccountFactory
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.ui.construct.UTF8Control
|
||||
@@ -35,19 +37,18 @@ object Proxies {
|
||||
}
|
||||
|
||||
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 {
|
||||
val OFFLINE_ACCOUNT_KEY = "offline"
|
||||
val YGGDRASIL_ACCOUNT_KEY = "yggdrasil"
|
||||
|
||||
val ACCOUNTS = listOf(OfflineAccount, YggdrasilAccount)
|
||||
val ACCOUNT_FACTORY = mapOf<String, AccountFactory<*>>(
|
||||
OFFLINE_ACCOUNT_KEY to OfflineAccount,
|
||||
YGGDRASIL_ACCOUNT_KEY to YggdrasilAccount
|
||||
OFFLINE_ACCOUNT_KEY to OfflineAccountFactory.INSTANCE,
|
||||
YGGDRASIL_ACCOUNT_KEY to YggdrasilAccountFactory.INSTANCE
|
||||
)
|
||||
|
||||
fun getAccountType(account: Account): String {
|
||||
|
||||
@@ -21,6 +21,10 @@ import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import org.jackhuang.hmcl.Main
|
||||
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 java.io.File
|
||||
import java.io.IOException
|
||||
@@ -70,7 +74,7 @@ class VersionSetting() {
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
@@ -232,28 +236,28 @@ class VersionSetting() {
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun toLaunchOptions(gameDir: File): LaunchOptions {
|
||||
return LaunchOptions(
|
||||
gameDir = gameDir,
|
||||
java = javaVersion ?: JavaVersion.fromCurrentEnvironment(),
|
||||
versionName = Main.TITLE,
|
||||
profileName = Main.TITLE,
|
||||
minecraftArgs = minecraftArgs,
|
||||
javaArgs = javaArgs,
|
||||
maxMemory = maxMemory,
|
||||
minMemory = minMemory,
|
||||
metaspace = permSize.toIntOrNull(),
|
||||
width = width,
|
||||
height = height,
|
||||
fullscreen = fullscreen,
|
||||
serverIp = serverIp,
|
||||
wrapper = wrapper,
|
||||
proxyHost = Settings.proxyHost,
|
||||
proxyPort = Settings.proxyPort,
|
||||
proxyUser = Settings.proxyUser,
|
||||
proxyPass = Settings.proxyPass,
|
||||
precalledCommand = precalledCommand,
|
||||
noGeneratedJVMArgs = noJVMArgs
|
||||
)
|
||||
return LaunchOptions.Builder()
|
||||
.setGameDir(gameDir)
|
||||
.setJava(javaVersion ?: JavaVersion.fromCurrentEnvironment())
|
||||
.setVersionName(Main.TITLE)
|
||||
.setProfileName(Main.TITLE)
|
||||
.setMinecraftArgs(minecraftArgs)
|
||||
.setJavaArgs(javaArgs)
|
||||
.setMaxMemory(maxMemory)
|
||||
.setMinMemory(minMemory)
|
||||
.setMetaspace(permSize.toIntOrNull())
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setFullscreen(fullscreen)
|
||||
.setServerIp(serverIp)
|
||||
.setWrapper(wrapper)
|
||||
.setProxyHost(Settings.proxyHost)
|
||||
.setProxyPort(Settings.proxyPort)
|
||||
.setProxyUser(Settings.proxyUser)
|
||||
.setProxyPass(Settings.proxyPass)
|
||||
.setPrecalledCommand(precalledCommand)
|
||||
.setNoGeneratedJVMArgs(noJVMArgs)
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object Serializer: JsonSerializer<VersionSetting>, JsonDeserializer<VersionSetting> {
|
||||
@@ -264,7 +268,7 @@ class VersionSetting() {
|
||||
addProperty("usesGlobal", src.usesGlobal)
|
||||
addProperty("javaArgs", src.javaArgs)
|
||||
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("permSize", src.permSize)
|
||||
addProperty("width", src.width)
|
||||
@@ -290,8 +294,8 @@ class VersionSetting() {
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||
|
||||
var maxMemoryN = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive, OS.SUGGESTED_MEMORY)
|
||||
if (maxMemoryN <= 0) maxMemoryN = OS.SUGGESTED_MEMORY
|
||||
var maxMemoryN = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive, OperatingSystem.SUGGESTED_MEMORY.toInt())
|
||||
if (maxMemoryN <= 0) maxMemoryN = OperatingSystem.SUGGESTED_MEMORY.toInt()
|
||||
|
||||
return VersionSetting().apply {
|
||||
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.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
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 {
|
||||
pgsSkin.isVisible = true
|
||||
AccountHelper.refreshSkinAsync(account)
|
||||
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
||||
.subscribe(Schedulers.javafx()) { loadSkin() }
|
||||
}
|
||||
AccountHelper.loadSkinAsync(account)
|
||||
.subscribe(Scheduler.JAVAFX) { loadSkin() }
|
||||
.subscribe(Schedulers.javafx()) { loadSkin() }
|
||||
}
|
||||
|
||||
if (account is OfflineAccount) { // Offline Account cannot be refreshed,
|
||||
|
||||
@@ -29,15 +29,17 @@ import javafx.scene.control.ToggleGroup
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||
import org.jackhuang.hmcl.auth.OfflineAccountFactory
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.taskResult
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||
import org.jackhuang.hmcl.util.taskResult
|
||||
|
||||
class AccountsPage() : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||
@@ -126,8 +128,8 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
taskResult("create_account") {
|
||||
try {
|
||||
val account = when (type) {
|
||||
0 -> OfflineAccount.fromUsername(username)
|
||||
1 -> YggdrasilAccount.fromUsername(username, password)
|
||||
0 -> OfflineAccountFactory.INSTANCE.fromUsername(username)
|
||||
1 -> YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@@ -136,7 +138,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
} catch (e: Exception) {
|
||||
e
|
||||
}
|
||||
}.subscribe(Scheduler.JAVAFX) {
|
||||
}.subscribe(Schedulers.javafx()) {
|
||||
val account: Any = it["create_account"]
|
||||
if (account is Account) {
|
||||
Settings.addAccount(account)
|
||||
|
||||
@@ -25,8 +25,8 @@ import javafx.scene.layout.Region
|
||||
import javafx.stage.Stage
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.task
|
||||
import org.jackhuang.hmcl.util.JavaVersion
|
||||
import org.jackhuang.hmcl.util.task
|
||||
|
||||
object Controllers {
|
||||
lateinit var scene: Scene private set
|
||||
|
||||
@@ -43,6 +43,7 @@ import javafx.scene.shape.Rectangle
|
||||
import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.logging.Level
|
||||
@@ -196,8 +197,8 @@ fun JFXMasonryPane.resetChildren(children: List<Node>) {
|
||||
fun openFolder(f: File) {
|
||||
f.mkdirs()
|
||||
val path = f.absolutePath
|
||||
when (OS.CURRENT_OS) {
|
||||
OS.OSX ->
|
||||
when (OperatingSystem.CURRENT_OS) {
|
||||
OperatingSystem.OSX ->
|
||||
try {
|
||||
Runtime.getRuntime().exec(arrayOf("/usr/bin/open", path));
|
||||
} catch (ex: IOException) {
|
||||
|
||||
@@ -20,13 +20,13 @@ package org.jackhuang.hmcl.ui
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.ScrollPane
|
||||
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.minecraftVersion
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.task
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.ui.download.InstallWizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.util.*
|
||||
|
||||
class InstallerController {
|
||||
@@ -59,9 +59,9 @@ class InstallerController {
|
||||
val removeAction = { _: InstallerItem ->
|
||||
val newList = LinkedList(version.libraries)
|
||||
newList.remove(library)
|
||||
VersionJSONSaveTask(profile.repository, version.copy(libraries = newList))
|
||||
VersionJsonSaveTask(profile.repository, version.setLibraries(newList))
|
||||
.with(task { profile.repository.refreshVersions() })
|
||||
.with(task(Scheduler.JAVAFX) { loadVersion(this.profile, this.versionId) })
|
||||
.with(task(Schedulers.javafx()) { loadVersion(this.profile, this.versionId) })
|
||||
.start()
|
||||
}
|
||||
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.ProfileLoadingEvent
|
||||
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.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||
import org.jackhuang.hmcl.util.channel
|
||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||
import org.jackhuang.hmcl.util.plusAssign
|
||||
import java.util.*
|
||||
|
||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
@@ -56,8 +58,8 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
.startCategory(i18n("ui.label.profile"))
|
||||
.add(profilePane)
|
||||
|
||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
EventBus.EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
Controllers.decorator.addMenuButton.setOnMouseClicked {
|
||||
Controllers.decorator.showPage(ProfilePage(null))
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jackhuang.hmcl.util.*
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Node
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
class LogWindow : Stage() {
|
||||
val fatalProperty = SimpleIntegerProperty(0)
|
||||
@@ -47,6 +48,7 @@ class LogWindow : Stage() {
|
||||
val debugProperty = SimpleIntegerProperty(0)
|
||||
|
||||
val impl = LogWindowImpl()
|
||||
val latch = CountDownLatch(1)
|
||||
|
||||
init {
|
||||
scene = Scene(impl, 800.0, 480.0)
|
||||
@@ -74,6 +76,8 @@ class LogWindow : Stage() {
|
||||
}
|
||||
}
|
||||
|
||||
fun waitForShown() = latch.await()
|
||||
|
||||
inner class LogWindowImpl: StackPane() {
|
||||
@FXML lateinit var webView: WebView
|
||||
@FXML lateinit var btnFatals: ToggleButton
|
||||
@@ -97,6 +101,7 @@ class LogWindow : Stage() {
|
||||
document = engine.document
|
||||
body = document.getElementsByTagName("body").item(0)
|
||||
engine.executeScript("limitedLogs=${Settings.logLines};")
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,17 +29,19 @@ import javafx.scene.image.Image
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
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.game.GameVersion.minecraftVersion
|
||||
import org.jackhuang.hmcl.game.LauncherHelper
|
||||
import org.jackhuang.hmcl.game.minecraftVersion
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.channel
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.plusAssign
|
||||
|
||||
/**
|
||||
* @see /assets/fxml/main.fxml
|
||||
@@ -59,9 +61,9 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
btnLaunch.limitWidth(40.0)
|
||||
btnLaunch.limitHeight(40.0)
|
||||
|
||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += { -> loadVersions() }
|
||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
EventBus.EVENT_BUS.channel<RefreshedVersionsEvent>() += { -> loadVersions() }
|
||||
EventBus.EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
||||
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
||||
|
||||
@@ -27,9 +27,10 @@ import javafx.stage.FileChooser
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.mod.ModManager
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.task
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.onChangeAndOperateWeakly
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.util.*
|
||||
|
||||
class ModController {
|
||||
@@ -77,7 +78,7 @@ class ModController {
|
||||
modManager.removeMods(versionId, modInfo)
|
||||
loadMods(modManager, versionId)
|
||||
}.apply {
|
||||
modInfo.activeProperty.onChange {
|
||||
modInfo.activeProperty().onChange {
|
||||
if (it)
|
||||
styleClass -= "disabled"
|
||||
else
|
||||
@@ -91,7 +92,7 @@ class ModController {
|
||||
runOnUiThread { rootPane.children += contentPane }
|
||||
it["list"] = list
|
||||
}
|
||||
}.subscribe(Scheduler.JAVAFX) { variables ->
|
||||
}.subscribe(Schedulers.javafx()) { variables ->
|
||||
parentTab.selectionModel.selectedItemProperty().onChangeAndOperateWeakly {
|
||||
if (it?.userData == this) {
|
||||
modPane.children.setAll(variables.get<List<ModItem>>("list"))
|
||||
@@ -106,6 +107,6 @@ class ModController {
|
||||
chooser.extensionFilters.setAll(FileChooser.ExtensionFilter("Mod", "*.jar", "*.zip", "*.litemod"))
|
||||
val res = chooser.showOpenDialog(Controllers.stage) ?: return
|
||||
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;"
|
||||
JFXDepthManager.setDepth(this, 1)
|
||||
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.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.ScrollPane
|
||||
import javafx.scene.control.Toggle
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.stage.DirectoryChooser
|
||||
import javafx.stage.FileChooser
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.setting.VersionSetting
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.task
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList
|
||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem
|
||||
import org.jackhuang.hmcl.ui.construct.NumberValidator
|
||||
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 {
|
||||
var lastVersionSetting: VersionSetting? = null
|
||||
@@ -74,7 +70,7 @@ class VersionSettingsController {
|
||||
lateinit var versionId: String
|
||||
|
||||
fun initialize() {
|
||||
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OS.TOTAL_MEMORY}MB"
|
||||
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OperatingSystem.TOTAL_MEMORY}MB"
|
||||
|
||||
scroll.smoothScrolling()
|
||||
|
||||
@@ -102,9 +98,9 @@ class VersionSettingsController {
|
||||
|
||||
task {
|
||||
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"))
|
||||
}
|
||||
|
||||
@@ -175,7 +171,7 @@ class VersionSettingsController {
|
||||
if (newValue == javaItem.radioCustom) { // Custom
|
||||
version.java = "Custom"
|
||||
} else {
|
||||
version.java = ((newValue as JFXRadioButton).userData as JavaVersion).longVersion
|
||||
version.java = ((newValue as JFXRadioButton).userData as JavaVersion).version
|
||||
}
|
||||
}
|
||||
javaItem.group.properties[javaGroupKey] = listener
|
||||
@@ -220,7 +216,7 @@ class VersionSettingsController {
|
||||
|
||||
private fun initJavaSubtitle(version: VersionSetting) {
|
||||
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) {
|
||||
|
||||
@@ -26,10 +26,11 @@ import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.auth.AuthInfo
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.InvalidCredentialsException
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.taskResult
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.util.taskResult
|
||||
|
||||
class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, private val success: (AuthInfo) -> Unit, private val failed: () -> Unit) : StackPane() {
|
||||
@FXML lateinit var lblUsername: Label
|
||||
@@ -54,12 +55,12 @@ class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, privat
|
||||
lblCreationWarning.text = ""
|
||||
taskResult("login") {
|
||||
try {
|
||||
val account = YggdrasilAccount.fromUsername(username, password)
|
||||
val account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||
account.logIn(Settings.proxy)
|
||||
} catch (e: Exception) {
|
||||
e
|
||||
}
|
||||
}.subscribe(Scheduler.JAVAFX) {
|
||||
}.subscribe(Schedulers.javafx()) {
|
||||
val account: Any = it["login"]
|
||||
if (account is AuthInfo) {
|
||||
success(account)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import javafx.scene.Node
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask
|
||||
import org.jackhuang.hmcl.game.HMCLModpackManifest
|
||||
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.Settings
|
||||
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.WizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.io.File
|
||||
|
||||
class DownloadWizardProvider(): WizardProvider() {
|
||||
@@ -79,11 +78,11 @@ class DownloadWizardProvider(): WizardProvider() {
|
||||
}
|
||||
|
||||
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 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()
|
||||
} with finalizeTask
|
||||
}.with(finalizeTask)
|
||||
}
|
||||
|
||||
override fun finish(settings: MutableMap<String, Any>): Any? {
|
||||
|
||||
@@ -2,20 +2,12 @@ package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import javafx.scene.Node
|
||||
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.mod.CurseForgeModpackInstallTask
|
||||
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
||||
import org.jackhuang.hmcl.mod.Modpack
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||
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.WizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
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() {
|
||||
@@ -27,20 +19,20 @@ class InstallWizardProvider(val profile: Profile, val gameVersion: String, val v
|
||||
var ret = task {}
|
||||
|
||||
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"))
|
||||
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"))
|
||||
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 {
|
||||
return when (step) {
|
||||
0 -> AdditionalInstallersPage(this, controller, profile.repository, BMCLAPIDownloadProvider)
|
||||
0 -> AdditionalInstallersPage(this, controller, profile.repository, BMCLAPIDownloadProvider.INSTANCE)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.task.TaskExecutor
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
||||
@@ -53,9 +54,9 @@ class VersionsPage(private val controller: WizardController, private val gameVer
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
executor = versionList.refreshAsync(downloadProvider).subscribe(Scheduler.JAVAFX) {
|
||||
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx()) {
|
||||
val versions = ArrayList(versionList.getVersions(gameVersion))
|
||||
versions.sortWith(RemoteVersion)
|
||||
versions.sortWith(RemoteVersion.RemoteVersionComparator.INSTANCE)
|
||||
for (version in versions) {
|
||||
list.items.add(VersionsPageItem(version))
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ class ExportWizardProvider(private val profile: Profile, private val version: St
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return HMCLModpackExportTask(profile.repository, version, settings[ModpackFileSelectionPage.MODPACK_FILE_SELECTION] as List<String>,
|
||||
Modpack(
|
||||
name = settings[ModpackInfoPage.MODPACK_NAME] as String,
|
||||
author = settings[ModpackInfoPage.MODPACK_AUTHOR] as String,
|
||||
version = settings[ModpackInfoPage.MODPACK_VERSION] as String,
|
||||
description = settings[ModpackInfoPage.MODPACK_DESCRIPTION] as String
|
||||
settings[ModpackInfoPage.MODPACK_NAME] as String,
|
||||
settings[ModpackInfoPage.MODPACK_AUTHOR] as String,
|
||||
settings[ModpackInfoPage.MODPACK_VERSION] as String,
|
||||
null,
|
||||
settings[ModpackInfoPage.MODPACK_DESCRIPTION] as String,
|
||||
null
|
||||
), settings[ModpackInfoPage.MODPACK_FILE] as File)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,7 @@ import javafx.application.Platform
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import org.jackhuang.hmcl.task.TaskExecutor
|
||||
import org.jackhuang.hmcl.task.TaskListener
|
||||
import org.jackhuang.hmcl.task.*
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
@@ -94,28 +91,28 @@ interface AbstractWizardDisplayer : WizardDisplayer {
|
||||
|
||||
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)
|
||||
}).executor().apply {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
taskListener = object : TaskListener {
|
||||
taskListener = object : TaskListener() {
|
||||
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) {
|
||||
Platform.runLater {
|
||||
label.text = task.title
|
||||
label.text = task.name
|
||||
++finishedTasks
|
||||
tasksBar.progressProperty().set(finishedTasks * 1.0 / totTask.get())
|
||||
tasksBar.progressProperty().set(finishedTasks * 1.0 / runningTasks)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailed(task: Task, throwable: Throwable) {
|
||||
Platform.runLater {
|
||||
label.text = task.title
|
||||
label.text = task.name
|
||||
++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 org.jackhuang.hmcl.util.*
|
||||
import java.net.URISyntaxException
|
||||
import org.jackhuang.hmcl.util.OS
|
||||
import org.jackhuang.hmcl.i18n
|
||||
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 java.net.Proxy
|
||||
import java.net.URI
|
||||
|
||||
class AppDataUpgrader : IUpgrader {
|
||||
@@ -123,18 +125,18 @@ class AppDataUpgrader : IUpgrader {
|
||||
else {
|
||||
var url = URL_PUBLISH
|
||||
if (map != null)
|
||||
if (map.containsKey(OS.CURRENT_OS.checkedName))
|
||||
url = map.get(OS.CURRENT_OS.checkedName)!!
|
||||
else if (map.containsKey(OS.UNKNOWN.checkedName))
|
||||
url = map.get(OS.UNKNOWN.checkedName)!!
|
||||
if (map.containsKey(OperatingSystem.CURRENT_OS.checkedName))
|
||||
url = map.get(OperatingSystem.CURRENT_OS.checkedName)!!
|
||||
else if (map.containsKey(OperatingSystem.UNKNOWN.checkedName))
|
||||
url = map.get(OperatingSystem.UNKNOWN.checkedName)!!
|
||||
try {
|
||||
java.awt.Desktop.getDesktop().browse(URI(url))
|
||||
} catch (e: URISyntaxException) {
|
||||
LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e)
|
||||
OS.setClipboard(url)
|
||||
OperatingSystem.setClipboard(url)
|
||||
} catch (e: IOException) {
|
||||
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() {
|
||||
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 {
|
||||
onDone += { event -> if (event.failed) tempFile.delete() }
|
||||
onDone() += { event -> if (event.isFailed) tempFile.delete() }
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val json = HashMap<String, String>()
|
||||
var f = getSelf(newestVersion)
|
||||
if (!f.parentFile.makeDirectory())
|
||||
if (!FileUtils.makeDirectory(f.parentFile))
|
||||
throw IOException("Failed to make directories: " + f.parent)
|
||||
|
||||
var i = 0
|
||||
@@ -187,15 +190,15 @@ class AppDataUpgrader : IUpgrader {
|
||||
}
|
||||
|
||||
class AppDataUpgraderJarTask(downloadLink: String, private val newestVersion: String, expectedHash: String) : Task() {
|
||||
override var title = "Upgrade"
|
||||
set(value) {}
|
||||
private val tempFile = File.createTempFile("hmcl", ".jar")
|
||||
|
||||
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() {
|
||||
val json = HashMap<String, String>()
|
||||
|
||||
@@ -21,6 +21,8 @@ import java.io.IOException
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import org.jackhuang.hmcl.task.TaskResult
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Constants.GSON
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.util.logging.Level
|
||||
|
||||
|
||||
@@ -58,10 +60,10 @@ class UpdateChecker(var base: VersionNumber, var type: String) {
|
||||
*/
|
||||
fun process(showMessage: Boolean): TaskResult<VersionNumber> {
|
||||
return object : TaskResult<VersionNumber>() {
|
||||
override val id = "update_checker.process"
|
||||
override fun getId() = "update_checker.process"
|
||||
override fun execute() {
|
||||
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!!)
|
||||
}
|
||||
|
||||
@@ -83,12 +85,12 @@ class UpdateChecker(var base: VersionNumber, var type: String) {
|
||||
@Synchronized
|
||||
fun requestDownloadLink(): 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() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (download_link == null)
|
||||
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) {
|
||||
LOG.log(Level.WARNING, "Failed to get update link.", e)
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -17,10 +17,25 @@
|
||||
*/
|
||||
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.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.concurrent.Callable
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
inline fun ignoreException(func: () -> Unit) {
|
||||
try {
|
||||
@@ -34,28 +49,6 @@ inline fun ignoreThrowable(func: () -> Unit) {
|
||||
} 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 isNotBlank(str: String?) = !isBlank(str)
|
||||
|
||||
@@ -84,42 +77,7 @@ fun String.asVersion(): String? {
|
||||
|
||||
fun Any?.toStringOrEmpty() = this?.toString().orEmpty()
|
||||
|
||||
fun parseParams(addBefore: String, objects: Collection<*>, addAfter: String): String {
|
||||
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 String.toURL() = URL(this)
|
||||
|
||||
fun Collection<String>.containsOne(vararg matcher: String): Boolean {
|
||||
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>) {
|
||||
if (update.getAndSet(newValue) == null) {
|
||||
UI_THREAD_SCHEDULER {
|
||||
UI_THREAD_SCHEDULER.accept(Runnable {
|
||||
val current = update.getAndSet(null)
|
||||
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
|
||||
* 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() {
|
||||
override val hidden: Boolean = true
|
||||
override val dependents: Collection<Task> = listOf(*tasks)
|
||||
public abstract class AccountFactory<T extends Account> {
|
||||
|
||||
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
|
||||
* 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() {
|
||||
LEGACY,
|
||||
MOJANG;
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class InvalidTokenException extends AuthenticationException {
|
||||
|
||||
fun fromName(name: String) = BY_NAME[name.toLowerCase()]
|
||||
fun fromLegacy(isLegacy: Boolean) = if (isLegacy) LEGACY else MOJANG
|
||||
private YggdrasilAccount account;
|
||||
|
||||
private val BY_NAME = HashMap<String, UserType>().apply {
|
||||
for (type in values())
|
||||
this[type.name.toLowerCase()] = type
|
||||
}
|
||||
public InvalidTokenException(YggdrasilAccount account) {
|
||||
super();
|
||||
this.account = account;
|
||||
}
|
||||
}
|
||||
|
||||
public YggdrasilAccount getAccount() {
|
||||
return account;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -15,12 +15,23 @@
|
||||
* 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.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() {
|
||||
override fun execute() {
|
||||
runnable(variables!!)
|
||||
private final String name;
|
||||
private final String value;
|
||||
|
||||
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
|
||||
* 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>() {
|
||||
override fun execute() {
|
||||
result = callable.call()
|
||||
private final String accessToken;
|
||||
private final String clientToken;
|
||||
|
||||
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
|
||||
* 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<*> {
|
||||
return downloadProvider.getVersionListById(id)
|
||||
public abstract DownloadProvider getDownloadProvider();
|
||||
|
||||
@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
|
||||
* 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 org.jackhuang.hmcl.game.Version
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import java.net.Proxy
|
||||
import java.net.Proxy;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
/**
|
||||
* Do everything that will connect to Internet.
|
||||
* Downloading Minecraft files.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
interface DependencyManager {
|
||||
public interface DependencyManager {
|
||||
|
||||
/**
|
||||
* The relied game repository.
|
||||
*/
|
||||
val repository: GameRepository
|
||||
GameRepository getGameRepository();
|
||||
|
||||
/**
|
||||
* The proxy that all network operations should go through.
|
||||
*/
|
||||
val proxy: Proxy
|
||||
Proxy getProxy();
|
||||
|
||||
/**
|
||||
* Check if the game is complete.
|
||||
@@ -43,12 +46,12 @@ interface DependencyManager {
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
fun gameBuilder(): GameBuilder
|
||||
GameBuilder gameBuilder();
|
||||
|
||||
/**
|
||||
* Install a library to a version.
|
||||
@@ -60,7 +63,7 @@ interface DependencyManager {
|
||||
* @param libraryVersion the version of being installed library.
|
||||
* @return the task to install the specific library.
|
||||
*/
|
||||
fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
|
||||
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
|
||||
|
||||
/**
|
||||
* Get registered version list.
|
||||
@@ -68,5 +71,5 @@ interface DependencyManager {
|
||||
* @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.
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
interface DownloadProvider {
|
||||
val libraryBaseURL: String
|
||||
val versionListURL: String
|
||||
val versionBaseURL: String
|
||||
val assetIndexBaseURL: String
|
||||
val assetBaseURL: String
|
||||
public interface DownloadProvider {
|
||||
|
||||
String getLibraryBaseURL();
|
||||
|
||||
String getVersionListURL();
|
||||
|
||||
String getVersionBaseURL();
|
||||
|
||||
String getAssetIndexBaseURL();
|
||||
|
||||
String getAssetBaseURL();
|
||||
|
||||
/**
|
||||
* Inject into original URL provided by Mojang and Forge.
|
||||
@@ -36,12 +43,13 @@ interface DownloadProvider {
|
||||
* @param baseURL original URL provided by Mojang and Forge.
|
||||
* @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"
|
||||
*
|
||||
* @param id the id of specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||
* @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
|
||||
* 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.
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
abstract class GameBuilder {
|
||||
var name: String = ""
|
||||
protected var gameVersion: String = ""
|
||||
protected var toolVersions = HashMap<String, String>()
|
||||
public abstract class GameBuilder {
|
||||
|
||||
protected String name = "";
|
||||
protected String gameVersion = "";
|
||||
protected Map<String, String> toolVersions = new HashMap<>();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The new game version name, for .minecraft/<version name>.
|
||||
*
|
||||
* @param name the name of new game version.
|
||||
*/
|
||||
fun name(name: String): GameBuilder {
|
||||
this.name = name
|
||||
return this
|
||||
public GameBuilder name(String name) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
fun gameVersion(version: String): GameBuilder {
|
||||
gameVersion = version
|
||||
return this
|
||||
public GameBuilder gameVersion(String version) {
|
||||
this.gameVersion = Objects.requireNonNull(version);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the core library id. i.e. "forge", "liteloader", "optifine"
|
||||
* @param version the version of the core library. For documents, you can first try [VersionList.versions]
|
||||
*/
|
||||
fun version(id: String, version: String): GameBuilder {
|
||||
toolVersions[id] = version
|
||||
return this
|
||||
public GameBuilder version(String id, String version) {
|
||||
if ("game".equals(id))
|
||||
gameVersion(version);
|
||||
else
|
||||
toolVersions.put(id, version);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* 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 org.jackhuang.hmcl.util.SimpleMultimap
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.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.
|
||||
* @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.
|
||||
* key: game version.
|
||||
* 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.
|
||||
*/
|
||||
val loaded = versions.isNotEmpty
|
||||
public boolean isLoaded() {
|
||||
return !versions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param downloadProvider DownloadProvider
|
||||
* @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>> {
|
||||
val ans = versions[gameVersion]
|
||||
return if (ans.isEmpty()) versions.values else ans
|
||||
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
|
||||
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
|
||||
return ans.isEmpty() ? versions.values() : ans;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,8 +65,8 @@ abstract class VersionList<T> {
|
||||
* @param gameVersion the Minecraft version that remote versions belong to
|
||||
* @return the collection of specific remote versions
|
||||
*/
|
||||
fun getVersions(gameVersion: String): Collection<RemoteVersion<T>> {
|
||||
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion))
|
||||
public final Collection<RemoteVersion<T>> getVersions(String gameVersion) {
|
||||
return Collections.unmodifiableCollection(getVersionsImpl(gameVersion));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,13 +76,11 @@ abstract class VersionList<T> {
|
||||
* @param remoteVersion the version of the remote version.
|
||||
* @return the specific remote version, null if it is not found.
|
||||
*/
|
||||
fun getVersion(gameVersion: String, remoteVersion: String): RemoteVersion<T>? {
|
||||
var result : RemoteVersion<T>? = null
|
||||
versions[gameVersion].forEach {
|
||||
if (it.selfVersion == remoteVersion)
|
||||
result = it
|
||||
}
|
||||
return result
|
||||
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
|
||||
RemoteVersion<T> result = null;
|
||||
for (RemoteVersion<T> it : versions.get(gameVersion))
|
||||
if (remoteVersion.equals(it.getSelfVersion()))
|
||||
result = it;
|
||||
return Optional.ofNullable(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
|
||||
* 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 org.jackhuang.hmcl.util.Immutable
|
||||
import org.jackhuang.hmcl.util.Validation
|
||||
import java.util.Date;
|
||||
import org.jackhuang.hmcl.game.ReleaseType;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
@Immutable
|
||||
class LoggingInfo @JvmOverloads constructor(
|
||||
@SerializedName("file")
|
||||
val file: IdDownloadInfo = IdDownloadInfo(),
|
||||
public final class GameRemoteVersionTag {
|
||||
|
||||
@SerializedName("argument")
|
||||
val argument: String = "",
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
|
||||
@SerializedName("type")
|
||||
val type: String = ""
|
||||
): Validation {
|
||||
|
||||
override fun validate() {
|
||||
file.validate()
|
||||
require(argument.isNotBlank(), { "LoggingInfo" })
|
||||
require(type.isNotBlank())
|
||||
public GameRemoteVersionTag() {
|
||||
this(ReleaseType.UNKNOWN, new Date());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* 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,
|
||||
HIGH,
|
||||
NORMAL,
|
||||
LOW,
|
||||
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