HMCL modpack import
This commit is contained in:
@@ -15,11 +15,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.setting
|
package org.jackhuang.hmcl.game
|
||||||
|
|
||||||
import javafx.scene.image.Image
|
import javafx.scene.image.Image
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask
|
import org.jackhuang.hmcl.task.FileDownloadTask
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
73
HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLModpack.kt
Normal file
73
HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLModpack.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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.game
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException
|
||||||
|
import org.jackhuang.hmcl.download.game.VersionJSONSaveTask
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack
|
||||||
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
|
import org.jackhuang.hmcl.task.Task
|
||||||
|
import org.jackhuang.hmcl.util.GSON
|
||||||
|
import org.jackhuang.hmcl.util.fromJson
|
||||||
|
import org.jackhuang.hmcl.util.readTextFromZipFile
|
||||||
|
import org.jackhuang.hmcl.util.unzipSubDirectory
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the manifest in a HMCL modpack.
|
||||||
|
*
|
||||||
|
* @param f a HMCL modpack file.
|
||||||
|
* @throws IOException if the file is not a valid zip file.
|
||||||
|
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||||
|
* @return the manifest of HMCL modpack.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class, JsonParseException::class)
|
||||||
|
fun readHMCLModpackManifest(f: File): Modpack {
|
||||||
|
val json = readTextFromZipFile(f, "modpack.json")
|
||||||
|
return GSON.fromJson<Modpack>(json)?.copy(manifest = HMCLModpackManifest) ?: throw JsonParseException("`modpack.json` not found. Not a valid CurseForge modpack.")
|
||||||
|
}
|
||||||
|
|
||||||
|
object HMCLModpackManifest
|
||||||
|
|
||||||
|
class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, private val name: String): Task() {
|
||||||
|
private val dependency = profile.dependency
|
||||||
|
private val repository = profile.repository
|
||||||
|
override val dependencies = mutableListOf<Task>()
|
||||||
|
override val dependents = mutableListOf<Task>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
check(!repository.hasVersion(name), { "Version $name already exists." })
|
||||||
|
val json = readTextFromZipFile(zipFile, "minecraft/pack.json")
|
||||||
|
var version = GSON.fromJson<Version>(json)!!
|
||||||
|
val jar = version.jar!!
|
||||||
|
version = version.copy(jar = null)
|
||||||
|
dependents += dependency.gameBuilder().name(name).gameVersion(jar).buildAsync()
|
||||||
|
dependencies += dependency.checkGameCompletionAsync(version)
|
||||||
|
dependencies += VersionJSONSaveTask(dependency, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val run = repository.getRunDirectory(name)
|
||||||
|
|
||||||
|
override fun execute() {
|
||||||
|
unzipSubDirectory(zipFile, run, "minecraft/", false)
|
||||||
|
val json = run.resolve("pack.json")
|
||||||
|
if (repository.getVersionJson(name) != json)
|
||||||
|
json.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.game
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack
|
||||||
|
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
||||||
|
import org.jackhuang.hmcl.util.readTextFromZipFileQuietly
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun readModpackManifest(f: File): Modpack {
|
||||||
|
try {
|
||||||
|
val manifest = readCurseForgeModpackManifest(f)
|
||||||
|
return Modpack(
|
||||||
|
name = manifest.name,
|
||||||
|
version = manifest.version,
|
||||||
|
author = manifest.author,
|
||||||
|
description = readTextFromZipFileQuietly(f, "modlist.html") ?: "No description",
|
||||||
|
manifest = manifest)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// ignore it, not a CurseForge modpack.
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val manifest = readHMCLModpackManifest(f)
|
||||||
|
return manifest
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// ignore it, not a HMCL modpack.
|
||||||
|
}
|
||||||
|
|
||||||
|
throw IllegalArgumentException("Modpack file $f is not supported.")
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ import javafx.scene.paint.Color
|
|||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.setting.AccountHelper
|
import org.jackhuang.hmcl.game.AccountHelper
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ package org.jackhuang.hmcl.ui.download
|
|||||||
|
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
|
import org.jackhuang.hmcl.game.HMCLModpackInstallTask
|
||||||
|
import org.jackhuang.hmcl.game.HMCLModpackManifest
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackInstallTask
|
import org.jackhuang.hmcl.mod.CurseForgeModpackInstallTask
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
||||||
|
import org.jackhuang.hmcl.mod.Modpack
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
@@ -62,11 +65,12 @@ class DownloadWizardProvider(): WizardProvider() {
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
val selectedFile = settings[ModpackPage.MODPACK_FILE] as? File? ?: return null
|
val selectedFile = settings[ModpackPage.MODPACK_FILE] as? File? ?: return null
|
||||||
val manifest = settings[ModpackPage.MODPACK_CURSEFORGE_MANIFEST] as? CurseForgeModpackManifest? ?: return null
|
val modpack = settings[ModpackPage.MODPACK_CURSEFORGE_MANIFEST] as? Modpack? ?: return null
|
||||||
val name = settings[ModpackPage.MODPACK_NAME] as? String? ?: return null
|
val name = settings[ModpackPage.MODPACK_NAME] as? String? ?: return null
|
||||||
|
|
||||||
profile.repository.markVersionAsModpack(name)
|
profile.repository.markVersionAsModpack(name)
|
||||||
return CurseForgeModpackInstallTask(profile.dependency, selectedFile, manifest, name) with task {
|
|
||||||
|
val finalizeTask = task {
|
||||||
profile.repository.refreshVersions()
|
profile.repository.refreshVersions()
|
||||||
val vs = profile.specializeVersionSetting(name)
|
val vs = profile.specializeVersionSetting(name)
|
||||||
profile.repository.undoMark(name)
|
profile.repository.undoMark(name)
|
||||||
@@ -74,6 +78,12 @@ class DownloadWizardProvider(): WizardProvider() {
|
|||||||
vs.gameDirType = EnumGameDirectory.VERSION_FOLDER
|
vs.gameDirType = EnumGameDirectory.VERSION_FOLDER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return when (modpack.manifest) {
|
||||||
|
is CurseForgeModpackManifest -> CurseForgeModpackInstallTask(profile.dependency, selectedFile, modpack.manifest as CurseForgeModpackManifest, name)
|
||||||
|
is HMCLModpackManifest -> HMCLModpackInstallTask(profile, selectedFile, name)
|
||||||
|
else -> throw Error()
|
||||||
|
} with finalizeTask
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finish(settings: MutableMap<String, Any>): Any? {
|
override fun finish(settings: MutableMap<String, Any>): Any? {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import javafx.fxml.FXML
|
|||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
|
import org.jackhuang.hmcl.game.readModpackManifest
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
||||||
import org.jackhuang.hmcl.setting.Profile
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
@@ -52,6 +53,7 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
|
|||||||
|
|
||||||
val chooser = FileChooser()
|
val chooser = FileChooser()
|
||||||
chooser.title = i18n("modpack.choose")
|
chooser.title = i18n("modpack.choose")
|
||||||
|
chooser.extensionFilters += FileChooser.ExtensionFilter("Zip", "*.zip")
|
||||||
val selectedFile = chooser.showOpenDialog(Controllers.stage)
|
val selectedFile = chooser.showOpenDialog(Controllers.stage)
|
||||||
if (selectedFile == null) Platform.runLater { controller.onFinish() }
|
if (selectedFile == null) Platform.runLater { controller.onFinish() }
|
||||||
else {
|
else {
|
||||||
@@ -65,14 +67,12 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val manifest = readCurseForgeModpackManifest(selectedFile)
|
val manifest = readModpackManifest(selectedFile)
|
||||||
controller.settings[MODPACK_CURSEFORGE_MANIFEST] = manifest
|
controller.settings[MODPACK_CURSEFORGE_MANIFEST] = manifest
|
||||||
lblName.text = manifest.name
|
lblName.text = manifest.name
|
||||||
lblVersion.text = manifest.version
|
lblVersion.text = manifest.version
|
||||||
lblAuthor.text = manifest.author
|
lblAuthor.text = manifest.author
|
||||||
} catch (e: IOException) {
|
} catch (e: Exception) {
|
||||||
// TODO
|
|
||||||
} catch (e: JsonParseException) {
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import java.io.File
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import java.util.zip.ZipFile
|
|
||||||
|
|
||||||
class CurseForgeModpackManifest @JvmOverloads constructor(
|
class CurseForgeModpackManifest @JvmOverloads constructor(
|
||||||
@SerializedName("manifestType")
|
@SerializedName("manifestType")
|
||||||
@@ -54,6 +53,8 @@ class CurseForgeModpackManifest @JvmOverloads constructor(
|
|||||||
check(manifestType == MINECRAFT_MODPACK, { "Only support Minecraft modpack" })
|
check(manifestType == MINECRAFT_MODPACK, { "Only support Minecraft modpack" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toModpack() = Modpack(name = name, author = author, version = version, description = "No description")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MINECRAFT_MODPACK = "minecraftModpack"
|
val MINECRAFT_MODPACK = "minecraftModpack"
|
||||||
}
|
}
|
||||||
@@ -101,14 +102,15 @@ class CurseForgeModpackManifestFile (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param f the CurseForge modpack file.
|
* @param f the CurseForge modpack file.
|
||||||
|
* @throws IOException if the file is not a valid zip file.
|
||||||
|
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||||
* @return the manifest.
|
* @return the manifest.
|
||||||
*/
|
*/
|
||||||
|
@Throws(IOException::class, JsonParseException::class)
|
||||||
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
||||||
ZipFile(f).use { zipFile ->
|
val json = readTextFromZipFile(f, "manifest.json")
|
||||||
val entry = zipFile.getEntry("manifest.json") ?: throw IOException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
return GSON.fromJson<CurseForgeModpackManifest>(json)
|
||||||
val json = zipFile.getInputStream(entry).readFullyAsString()
|
?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||||
return GSON.fromJson<CurseForgeModpackManifest>(json) ?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,8 +125,7 @@ fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
|||||||
class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDependencyManager, private val zipFile: File, private val manifest: CurseForgeModpackManifest, private val name: String): Task() {
|
class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDependencyManager, private val zipFile: File, private val manifest: CurseForgeModpackManifest, private val name: String): Task() {
|
||||||
val repository = dependencyManager.repository
|
val repository = dependencyManager.repository
|
||||||
init {
|
init {
|
||||||
if (repository.hasVersion(name))
|
check(!repository.hasVersion(name), { "Version $name already exists." })
|
||||||
throw IllegalStateException("Version $name already exists.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val root = repository.getVersionRoot(name)
|
val root = repository.getVersionRoot(name)
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.mod
|
package org.jackhuang.hmcl.mod
|
||||||
|
|
||||||
import java.io.File
|
data class Modpack @JvmOverloads constructor(
|
||||||
|
val name: String = "",
|
||||||
class Modpack(val file: File) {
|
val author: String = "",
|
||||||
|
val version: String = "",
|
||||||
}
|
val description: String = "",
|
||||||
|
val manifest: Any? = null
|
||||||
|
)
|
||||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.util
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipFile
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
@@ -194,4 +195,35 @@ fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExisten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the text content of a file in zip.
|
||||||
|
*
|
||||||
|
* @param f the zip file
|
||||||
|
* @param location the location of the text in zip file, something like A/B/C/D.txt
|
||||||
|
* @throws IOException if the file is not a valid zip file.
|
||||||
|
* @return the content of given file.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun readTextFromZipFile(f: File, location: String): String {
|
||||||
|
ZipFile(f).use { zipFile ->
|
||||||
|
val entry = zipFile.getEntry(location) ?: throw IOException("`$location` not found.")
|
||||||
|
return zipFile.getInputStream(entry).readFullyAsString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the text content of a file in zip.
|
||||||
|
*
|
||||||
|
* @param f the zip file
|
||||||
|
* @param location the location of the text in zip file, something like A/B/C/D.txt
|
||||||
|
* @return the content of given file.
|
||||||
|
*/
|
||||||
|
fun readTextFromZipFileQuietly(f: File, location: String): String? {
|
||||||
|
try {
|
||||||
|
return readTextFromZipFile(f, location)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user