HMCL modpack import
This commit is contained in:
@@ -15,11 +15,12 @@
|
||||
* 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.setting
|
||||
package org.jackhuang.hmcl.game
|
||||
|
||||
import javafx.scene.image.Image
|
||||
import org.jackhuang.hmcl.Main
|
||||
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.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.OfflineAccount
|
||||
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.task.Scheduler
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@@ -19,8 +19,11 @@ 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.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
|
||||
@@ -62,11 +65,12 @@ class DownloadWizardProvider(): WizardProvider() {
|
||||
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
|
||||
|
||||
profile.repository.markVersionAsModpack(name)
|
||||
return CurseForgeModpackInstallTask(profile.dependency, selectedFile, manifest, name) with task {
|
||||
|
||||
val finalizeTask = task {
|
||||
profile.repository.refreshVersions()
|
||||
val vs = profile.specializeVersionSetting(name)
|
||||
profile.repository.undoMark(name)
|
||||
@@ -74,6 +78,12 @@ class DownloadWizardProvider(): WizardProvider() {
|
||||
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? {
|
||||
|
||||
@@ -25,6 +25,7 @@ import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.stage.FileChooser
|
||||
import org.jackhuang.hmcl.game.readModpackManifest
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.mod.readCurseForgeModpackManifest
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
@@ -52,6 +53,7 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
|
||||
|
||||
val chooser = FileChooser()
|
||||
chooser.title = i18n("modpack.choose")
|
||||
chooser.extensionFilters += FileChooser.ExtensionFilter("Zip", "*.zip")
|
||||
val selectedFile = chooser.showOpenDialog(Controllers.stage)
|
||||
if (selectedFile == null) Platform.runLater { controller.onFinish() }
|
||||
else {
|
||||
@@ -65,14 +67,12 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
|
||||
}
|
||||
|
||||
try {
|
||||
val manifest = readCurseForgeModpackManifest(selectedFile)
|
||||
val manifest = readModpackManifest(selectedFile)
|
||||
controller.settings[MODPACK_CURSEFORGE_MANIFEST] = manifest
|
||||
lblName.text = manifest.name
|
||||
lblVersion.text = manifest.version
|
||||
lblAuthor.text = manifest.author
|
||||
} catch (e: IOException) {
|
||||
// TODO
|
||||
} catch (e: JsonParseException) {
|
||||
} catch (e: Exception) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.util.logging.Level
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class CurseForgeModpackManifest @JvmOverloads constructor(
|
||||
@SerializedName("manifestType")
|
||||
@@ -54,6 +53,8 @@ class CurseForgeModpackManifest @JvmOverloads constructor(
|
||||
check(manifestType == MINECRAFT_MODPACK, { "Only support Minecraft modpack" })
|
||||
}
|
||||
|
||||
fun toModpack() = Modpack(name = name, author = author, version = version, description = "No description")
|
||||
|
||||
companion object {
|
||||
val MINECRAFT_MODPACK = "minecraftModpack"
|
||||
}
|
||||
@@ -101,14 +102,15 @@ class CurseForgeModpackManifestFile (
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
@Throws(IOException::class, JsonParseException::class)
|
||||
fun readCurseForgeModpackManifest(f: File): CurseForgeModpackManifest {
|
||||
ZipFile(f).use { zipFile ->
|
||||
val entry = zipFile.getEntry("manifest.json") ?: throw IOException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||
val json = zipFile.getInputStream(entry).readFullyAsString()
|
||||
return GSON.fromJson<CurseForgeModpackManifest>(json) ?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||
}
|
||||
val json = readTextFromZipFile(f, "manifest.json")
|
||||
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() {
|
||||
val repository = dependencyManager.repository
|
||||
init {
|
||||
if (repository.hasVersion(name))
|
||||
throw IllegalStateException("Version $name already exists.")
|
||||
check(!repository.hasVersion(name), { "Version $name already exists." })
|
||||
}
|
||||
|
||||
val root = repository.getVersionRoot(name)
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod
|
||||
|
||||
import java.io.File
|
||||
|
||||
class Modpack(val file: File) {
|
||||
|
||||
}
|
||||
data class Modpack @JvmOverloads constructor(
|
||||
val name: String = "",
|
||||
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.IOException
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
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