Fixed logging spaces and tabs missing, unclickable version item, removing 'logging' element when installing forge and liteloader
This commit is contained in:
@@ -51,9 +51,9 @@ import com.sun.javafx.scene.layout.region.BorderStyleConverter.HIDDEN
|
|||||||
*/
|
*/
|
||||||
@Throws(IOException::class, JsonParseException::class)
|
@Throws(IOException::class, JsonParseException::class)
|
||||||
fun readHMCLModpackManifest(f: File): Modpack {
|
fun readHMCLModpackManifest(f: File): Modpack {
|
||||||
val manifestJson = readTextFromZipFile(f, "modpack.json")
|
val manifestJson = f.readTextZipEntry("modpack.json")
|
||||||
val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.")
|
val manifest = GSON.fromJson<Modpack>(manifestJson) ?: throw JsonParseException("`modpack.json` not found. $f is not a valid HMCL modpack.")
|
||||||
val gameJson = readTextFromZipFile(f, "minecraft/pack.json")
|
val gameJson = f.readTextZipEntry("minecraft/pack.json")
|
||||||
val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.")
|
val game = GSON.fromJson<Version>(gameJson) ?: throw JsonParseException("`minecraft/pack.json` not found. $f iot a valid HMCL modpack.")
|
||||||
return if (game.jar == null)
|
return if (game.jar == null)
|
||||||
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
|
if (manifest.gameVersion.isNullOrBlank()) throw JsonParseException("Cannot recognize the game version of modpack $f.")
|
||||||
@@ -71,7 +71,7 @@ class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, privat
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
check(!repository.hasVersion(name), { "Version $name already exists." })
|
check(!repository.hasVersion(name), { "Version $name already exists." })
|
||||||
val json = readTextFromZipFile(zipFile, "minecraft/pack.json")
|
val json = zipFile.readTextZipEntry("minecraft/pack.json")
|
||||||
var version = GSON.fromJson<Version>(json)!!
|
var version = GSON.fromJson<Version>(json)!!
|
||||||
version = version.copy(jar = null)
|
version = version.copy(jar = null)
|
||||||
dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync()
|
dependents += dependency.gameBuilder().name(name).gameVersion(modpack.gameVersion!!).buildAsync()
|
||||||
@@ -81,10 +81,7 @@ class HMCLModpackInstallTask(profile: Profile, private val zipFile: File, privat
|
|||||||
private val run = repository.getRunDirectory(name)
|
private val run = repository.getRunDirectory(name)
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
unzipSubDirectory(zipFile, run, "minecraft/", false)
|
zipFile.uncompressTo(run, "minecraft/", callback = { it != "minecraft/pack.json" }, ignoreExistentFile = false)
|
||||||
val json = run.resolve("pack.json")
|
|
||||||
if (repository.getVersionJson(name) != json)
|
|
||||||
json.delete()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,10 @@ object Settings {
|
|||||||
else {
|
else {
|
||||||
System.setProperty("http.proxyHost", proxyHost)
|
System.setProperty("http.proxyHost", proxyHost)
|
||||||
System.setProperty("http.proxyPort", proxyPort)
|
System.setProperty("http.proxyPort", proxyPort)
|
||||||
proxy = Proxy(proxyType, InetSocketAddress(host, port))
|
if (proxyType == Proxy.Type.DIRECT)
|
||||||
|
proxy = Proxy.NO_PROXY
|
||||||
|
else
|
||||||
|
proxy = Proxy(proxyType, InetSocketAddress(host, port))
|
||||||
|
|
||||||
val user = proxyUser
|
val user = proxyUser
|
||||||
val pass = proxyPass
|
val pass = proxyPass
|
||||||
|
|||||||
@@ -30,13 +30,11 @@ import javafx.scene.layout.StackPane
|
|||||||
import javafx.scene.web.WebEngine
|
import javafx.scene.web.WebEngine
|
||||||
import javafx.scene.web.WebView
|
import javafx.scene.web.WebView
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
|
import netscape.javascript.JSObject
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper
|
import org.jackhuang.hmcl.game.LauncherHelper
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.inc
|
|
||||||
import org.jackhuang.hmcl.util.onChange
|
|
||||||
import org.jackhuang.hmcl.util.readFullyAsString
|
|
||||||
import org.w3c.dom.Document
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
@@ -59,7 +57,10 @@ class LogWindow : Stage() {
|
|||||||
|
|
||||||
fun logLine(line: String, level: Log4jLevel) {
|
fun logLine(line: String, level: Log4jLevel) {
|
||||||
impl.body.appendChild(impl.engine.document.createElement("div").apply {
|
impl.body.appendChild(impl.engine.document.createElement("div").apply {
|
||||||
textContent = line
|
// a <pre> element to prevent multiple spaces and tabs being removed.
|
||||||
|
appendChild(impl.engine.document.createElement("pre").apply {
|
||||||
|
textContent = line
|
||||||
|
})
|
||||||
})
|
})
|
||||||
impl.engine.executeScript("checkNewLog(\"${level.name.toLowerCase()}\");scrollToBottom();")
|
impl.engine.executeScript("checkNewLog(\"${level.name.toLowerCase()}\");scrollToBottom();")
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class ModpackPage(private val controller: WizardController): StackPane(), Wizard
|
|||||||
lblName.text = manifest!!.name
|
lblName.text = manifest!!.name
|
||||||
lblVersion.text = manifest!!.version
|
lblVersion.text = manifest!!.version
|
||||||
lblAuthor.text = manifest!!.author
|
lblAuthor.text = manifest!!.author
|
||||||
txtModpackName.text = manifest!!.name + "-" + manifest!!.version
|
txtModpackName.text = manifest!!.name + (if (manifest!!.version.isNullOrBlank()) "" else ("-" + manifest!!.version))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// TODO
|
// TODO
|
||||||
txtModpackName.text = i18n("modpack.task.install.error")
|
txtModpackName.text = i18n("modpack.task.install.error")
|
||||||
|
|||||||
@@ -5,14 +5,13 @@
|
|||||||
<?import javafx.scene.control.ScrollPane?>
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<fx:root
|
<fx:root
|
||||||
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||||
styleClass="transparent" type="StackPane"
|
styleClass="transparent" type="StackPane" pickOnBounds="false"
|
||||||
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
|
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
|
||||||
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
|
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
|
||||||
</JFXMasonryPane>
|
</JFXMasonryPane>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
|
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
|
||||||
<!--JFXSpinner fx:id="spinner" style="-fx-radius:10" styleClass="materialDesign-purple, first-spinner" /-->
|
|
||||||
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh"
|
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh"
|
||||||
style="-fx-background-color:#5264AE;-fx-background-radius: 50px;">
|
style="-fx-background-color:#5264AE;-fx-background-radius: 50px;">
|
||||||
<graphic>
|
<graphic>
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
<?import com.jfoenix.controls.JFXRadioButton?>
|
<?import com.jfoenix.controls.JFXRadioButton?>
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
type="StackPane">
|
type="StackPane" pickOnBounds="false">
|
||||||
<VBox fx:id="content">
|
<VBox fx:id="content" pickOnBounds="false">
|
||||||
<StackPane fx:id="header" VBox.vgrow="ALWAYS">
|
<StackPane fx:id="header" VBox.vgrow="ALWAYS" pickOnBounds="false">
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<top>
|
<top>
|
||||||
<HBox alignment="CENTER_RIGHT">
|
<HBox alignment="CENTER_RIGHT">
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
|
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40" pickOnBounds="false">
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<left>
|
<left>
|
||||||
<HBox spacing="8">
|
<HBox spacing="8">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div > pre {
|
||||||
font: ${FONT};
|
font: ${FONT};
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ object BMCLAPIDownloadProvider : DownloadProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun injectURL(baseURL: String): String = baseURL
|
override fun injectURL(baseURL: String): String = baseURL
|
||||||
.replace("https://launchermeta.mojang.com", "http://bmclapi2.bangbang93.com")
|
.replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||||
.replace("https://launcher.mojang.com", "http://bmclapi2.bangbang93.com")
|
.replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||||
.replace("https://libraries.minecraft.net", "http://bmclapi2.bangbang93.com/libraries")
|
.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||||
.replace("http://files.minecraftforge.net/maven", "http://bmclapi2.bangbang93.com/maven")
|
.replace("http://files.minecraftforge.net/maven", "https://bmclapi2.bangbang93.com/maven")
|
||||||
.replace("http://dl.liteloader.com/versions/versions.json", "http://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
|
.replace("http://dl.liteloader.com/versions/versions.json", "httsp://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
|
||||||
.replace("http://dl.liteloader.com/versions", "http://bmclapi2.bangbang93.com/maven")
|
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven")
|
||||||
}
|
}
|
||||||
@@ -46,8 +46,6 @@ object MojangDownloadProvider : DownloadProvider() {
|
|||||||
override fun injectURL(baseURL: String): String {
|
override fun injectURL(baseURL: String): String {
|
||||||
if (baseURL.endsWith("net/minecraftforge/forge/json"))
|
if (baseURL.endsWith("net/minecraftforge/forge/json"))
|
||||||
return baseURL
|
return baseURL
|
||||||
else if (Locale.getDefault() == Locale.CHINA)
|
|
||||||
return baseURL.replace("http://files.minecraftforge.net/maven", "http://maven.aliyun.com/nexus/content/groups/public");
|
|
||||||
else
|
else
|
||||||
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven")
|
return baseURL.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
val versionProvider = SimpleVersionProvider()
|
val versionProvider = SimpleVersionProvider()
|
||||||
versionProvider.addVersion(version)
|
versionProvider.addVersion(version)
|
||||||
|
|
||||||
result = installProfile.versionInfo!!.copy(inheritsFrom = version.id).resolve(versionProvider).copy(id = version.id)
|
result = installProfile.versionInfo!!.copy(inheritsFrom = version.id).resolve(versionProvider).copy(id = version.id, logging = emptyMap())
|
||||||
dependencies += GameLibrariesTask(dependencyManager, installProfile.versionInfo)
|
dependencies += GameLibrariesTask(dependencyManager, installProfile.versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
result = version.copy(
|
result = version.copy(
|
||||||
mainClass = "net.minecraft.launchwrapper.Launch",
|
mainClass = "net.minecraft.launchwrapper.Launch",
|
||||||
minecraftArguments = version.minecraftArguments + " --tweakClass " + remote.tag.tweakClass,
|
minecraftArguments = version.minecraftArguments + " --tweakClass " + remote.tag.tweakClass,
|
||||||
libraries = merge(tempVersion.libraries, version.libraries)
|
libraries = merge(tempVersion.libraries, version.libraries),
|
||||||
|
logging = emptyMap()
|
||||||
)
|
)
|
||||||
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ open class Library(
|
|||||||
|
|
||||||
companion object LibrarySerializer : JsonDeserializer<Library>, JsonSerializer<Library> {
|
companion object LibrarySerializer : JsonDeserializer<Library>, JsonSerializer<Library> {
|
||||||
fun fromName(name: String, url: String? = null, downloads: LibrariesDownloadInfo? = null, extract: ExtractRules? = null, natives: Map<OS, String>? = null, rules: List<CompatibilityRule>? = null): Library {
|
fun fromName(name: String, url: String? = null, downloads: LibrariesDownloadInfo? = null, extract: ExtractRules? = null, natives: Map<OS, String>? = null, rules: List<CompatibilityRule>? = null): Library {
|
||||||
val arr = name.split(":".toRegex(), 3)
|
val arr = name.split(":".toRegex(), 4)
|
||||||
if (arr.size != 3 && arr.size != 4)
|
if (arr.size != 3 && arr.size != 4)
|
||||||
throw IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version.")
|
throw IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version.")
|
||||||
return Library(
|
return Library(
|
||||||
|
|||||||
@@ -214,11 +214,11 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
open fun decompressNatives() {
|
open fun decompressNatives() {
|
||||||
version.libraries.filter { it.isNative }.forEach { library ->
|
version.libraries.filter { it.isNative }.forEach { library ->
|
||||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||||
unzip(zip = repository.getLibraryFile(version, library),
|
repository.getLibraryFile(version, library).uncompressTo(
|
||||||
dest = native,
|
dest = native,
|
||||||
callback = if (library.extract == null) null
|
callback = if (library.extract == null) null
|
||||||
else library.extract!!::shouldExtract,
|
else library.extract!!::shouldExtract,
|
||||||
ignoreExistsFile = false)
|
ignoreExistentFile = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,10 +230,13 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
decompressNatives()
|
decompressNatives()
|
||||||
|
|
||||||
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
|
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
|
||||||
val process = Runtime.getRuntime().exec(options.precalledCommand)
|
try {
|
||||||
ignoreException {
|
val process = Runtime.getRuntime().exec(options.precalledCommand)
|
||||||
if (process.isAlive)
|
if (process.isAlive)
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// TODO: alert precalledCommand is wrong.
|
||||||
|
// rethrow InterruptedException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +290,14 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
private fun startMonitors(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
||||||
|
val enablesLoggingInfo = version.logging?.containsKey(DownloadType.CLIENT) ?: false
|
||||||
|
if (enablesLoggingInfo)
|
||||||
|
startMonitorsWithLoggingInfo(managedProcess, processListener, isDaemon)
|
||||||
|
else
|
||||||
|
startMonitorsWithoutLoggingInfo(managedProcess, processListener, isDaemon)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startMonitorsWithLoggingInfo(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
||||||
processListener.setProcess(managedProcess)
|
processListener.setProcess(managedProcess)
|
||||||
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() }
|
val logHandler = Log4jHandler { line, level -> processListener.onLog(line, level); managedProcess.lines += line }.apply { start() }
|
||||||
managedProcess.relatedThreads += logHandler
|
managedProcess.relatedThreads += logHandler
|
||||||
@@ -297,6 +308,15 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run)
|
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> logHandler.onStopped(); processListener.onExit(exitCode, exitType) })::run)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startMonitorsWithoutLoggingInfo(managedProcess: ManagedProcess, processListener: ProcessListener, isDaemon: Boolean = true) {
|
||||||
|
processListener.setProcess(managedProcess)
|
||||||
|
val stdout = thread(name = "stdout-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.inputStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.guessLevel(it) ?: Log4jLevel.INFO); managedProcess.lines += it })::run)
|
||||||
|
managedProcess.relatedThreads += stdout
|
||||||
|
val stderr = thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(managedProcess.process.errorStream, { processListener.onLog(it + OS.LINE_SEPARATOR, Log4jLevel.ERROR); managedProcess.lines += it })::run)
|
||||||
|
managedProcess.relatedThreads += stderr
|
||||||
|
managedProcess.relatedThreads += thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(managedProcess, listOf(stdout, stderr), { exitCode, exitType -> processListener.onExit(exitCode, exitType) })::run)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val LAUNCH_ASYNC_ID = "process"
|
const val LAUNCH_ASYNC_ID = "process"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class CurseForgeModpackManifestFile (
|
|||||||
*/
|
*/
|
||||||
@Throws(IOException::class, JsonParseException::class)
|
@Throws(IOException::class, JsonParseException::class)
|
||||||
fun readCurseForgeModpackManifest(f: File): Modpack {
|
fun readCurseForgeModpackManifest(f: File): Modpack {
|
||||||
val json = readTextFromZipFile(f, "manifest.json")
|
val json = f.readTextZipEntry("manifest.json")
|
||||||
val manifest = GSON.fromJson<CurseForgeModpackManifest>(json)
|
val manifest = GSON.fromJson<CurseForgeModpackManifest>(json)
|
||||||
?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
?: throw JsonParseException("`manifest.json` not found. Not a valid CurseForge modpack.")
|
||||||
return Modpack(
|
return Modpack(
|
||||||
@@ -114,7 +114,7 @@ fun readCurseForgeModpackManifest(f: File): Modpack {
|
|||||||
version = manifest.version,
|
version = manifest.version,
|
||||||
author = manifest.author,
|
author = manifest.author,
|
||||||
gameVersion = manifest.minecraft.gameVersion,
|
gameVersion = manifest.minecraft.gameVersion,
|
||||||
description = readTextFromZipFileQuietly(f, "modlist.html") ?: "No description",
|
description = f.readTextZipEntryQuietly("modlist.html") ?: "No description",
|
||||||
manifest = manifest)
|
manifest = manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ class CurseForgeModpackInstallTask(private val dependencyManager: DefaultDepende
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
unzipSubDirectory(zipFile, run, manifest.overrides)
|
zipFile.uncompressTo(run, subDirectory = manifest.overrides)
|
||||||
|
|
||||||
var finished = 0
|
var finished = 0
|
||||||
for (f in manifest.files) {
|
for (f in manifest.files) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class InstancePatch @JvmOverloads constructor(
|
|||||||
val libraries: List<Library> = emptyList()
|
val libraries: List<Library> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
class InstanceConfiguration(contentStream: InputStream) {
|
class InstanceConfiguration(defaultName: String, contentStream: InputStream) {
|
||||||
/**
|
/**
|
||||||
* The instance's name
|
* The instance's name
|
||||||
*/
|
*/
|
||||||
@@ -183,15 +183,17 @@ class InstanceConfiguration(contentStream: InputStream) {
|
|||||||
showConsole = p.getProperty("ShowConsole") == "true"
|
showConsole = p.getProperty("ShowConsole") == "true"
|
||||||
showConsoleOnError = p.getProperty("ShowConsoleOnError") == "true"
|
showConsoleOnError = p.getProperty("ShowConsoleOnError") == "true"
|
||||||
wrapperCommand = p.getProperty("WrapperCommand")
|
wrapperCommand = p.getProperty("WrapperCommand")
|
||||||
name = p.getProperty("name")
|
name = defaultName
|
||||||
notes = p.getProperty("notes")
|
notes = p.getProperty("notes") ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readMMCModpackManifest(f: File): Modpack {
|
fun readMMCModpackManifest(f: File): Modpack {
|
||||||
ZipFile(f).use { zipFile ->
|
ZipFile(f).use { zipFile ->
|
||||||
val entry = zipFile.getEntry("instance.cfg") ?: throw IOException("`instance.cfg` not found, $f is not a valid MultiMC modpack.")
|
val firstEntry = zipFile.entries.nextElement()
|
||||||
val cfg = InstanceConfiguration(zipFile.getInputStream(entry))
|
val name = firstEntry.name.substringBefore("/")
|
||||||
|
val entry = zipFile.getEntry("$name/instance.cfg") ?: throw IOException("`instance.cfg` not found, $f is not a valid MultiMC modpack.")
|
||||||
|
val cfg = InstanceConfiguration(name, zipFile.getInputStream(entry))
|
||||||
return Modpack(
|
return Modpack(
|
||||||
name = cfg.name,
|
name = cfg.name,
|
||||||
version = "",
|
version = "",
|
||||||
@@ -217,12 +219,12 @@ class MMCModpackInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
var version = repository.readVersionJson(name)!!
|
var version = repository.readVersionJson(name)!!
|
||||||
unzipSubDirectory(zipFile, run, "minecraft/", false)
|
zipFile.uncompressTo(run, subDirectory = "${manifest.name}/minecraft/", ignoreExistentFile = false, allowStoredEntriesWithDataDescriptor = true)
|
||||||
|
|
||||||
ZipFile(zipFile).use { zip ->
|
ZipFile(zipFile).use { zip ->
|
||||||
for (entry in zip.entries) {
|
for (entry in zip.entries) {
|
||||||
// ensure that this entry is in folder 'patches' and is a json file.
|
// ensure that this entry is in folder 'patches' and is a json file.
|
||||||
if (!entry.isDirectory && entry.name.startsWith("patches/") && entry.name.endsWith(".json")) {
|
if (!entry.isDirectory && entry.name.startsWith("${manifest.name}/patches/") && entry.name.endsWith(".json")) {
|
||||||
val patch = GSON.fromJson<InstancePatch>(zip.getInputStream(entry).readFullyAsString())!!
|
val patch = GSON.fromJson<InstancePatch>(zip.getInputStream(entry).readFullyAsString())!!
|
||||||
val args = StringBuilder(version.minecraftArguments)
|
val args = StringBuilder(version.minecraftArguments)
|
||||||
for (arg in patch.tweakers)
|
for (arg in patch.tweakers)
|
||||||
|
|||||||
@@ -34,21 +34,22 @@ import java.io.IOException
|
|||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun zip(src: File, destZip: File, pathNameCallback: ((String, Boolean) -> String?)? = null) {
|
fun File.zipTo(destZip: File, pathNameCallback: ((String, Boolean) -> String?)? = null) {
|
||||||
ZipArchiveOutputStream(destZip.outputStream()).use { zos ->
|
ZipArchiveOutputStream(destZip.outputStream()).use { zos ->
|
||||||
val basePath: String
|
val basePath: String
|
||||||
if (src.isDirectory)
|
if (this.isDirectory)
|
||||||
basePath = src.path
|
basePath = this.path
|
||||||
else
|
else
|
||||||
//直接压缩单个文件时,取父目录
|
//直接压缩单个文件时,取父目录
|
||||||
basePath = src.parent
|
basePath = this.parent
|
||||||
zipFile(src, basePath, zos, pathNameCallback)
|
zipFile(this, basePath, zos, pathNameCallback)
|
||||||
zos.closeArchiveEntry()
|
zos.closeArchiveEntry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zip file.
|
* Zip file.
|
||||||
|
*
|
||||||
* @param src source directory to be compressed.
|
* @param src source directory to be compressed.
|
||||||
* @param basePath the file directory to be compressed, if [src] is a file, this is the parent directory of [src]
|
* @param basePath the file directory to be compressed, if [src] is a file, this is the parent directory of [src]
|
||||||
* @param zos the [ZipOutputStream] of dest zip file.
|
* @param zos the [ZipOutputStream] of dest zip file.
|
||||||
@@ -92,69 +93,21 @@ private fun zipFile(src: File,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decompress the given zip file to a directory.
|
* Decompress the given zip file to a directory.
|
||||||
* @param zip the source zip file.
|
*
|
||||||
* @param dest the dest directory.
|
|
||||||
* @param callback will be called for every entry in the zip file, returns false if you dont want this file unzipped.
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun unzip(zip: File, dest: File, callback: ((String) -> Boolean)? = null, ignoreExistsFile: Boolean = true) {
|
|
||||||
val buf = ByteArray(1024)
|
|
||||||
dest.mkdirs()
|
|
||||||
ZipArchiveInputStream(zip.inputStream()).use { zipFile ->
|
|
||||||
if (zip.exists()) {
|
|
||||||
val strPath = dest.absolutePath
|
|
||||||
while (true) {
|
|
||||||
val zipEnt = zipFile.nextEntry ?: break
|
|
||||||
var strtemp: String
|
|
||||||
var gbkPath = zipEnt.name
|
|
||||||
if (callback != null)
|
|
||||||
if (!callback.invoke(gbkPath))
|
|
||||||
continue
|
|
||||||
if (zipEnt.isDirectory) {
|
|
||||||
strtemp = strPath + File.separator + gbkPath
|
|
||||||
val dir = File(strtemp)
|
|
||||||
dir.mkdirs()
|
|
||||||
} else {
|
|
||||||
//读写文件
|
|
||||||
gbkPath = zipEnt.name
|
|
||||||
strtemp = strPath + File.separator + gbkPath
|
|
||||||
//建目录
|
|
||||||
val strsubdir = gbkPath
|
|
||||||
for (i in 0 until strsubdir.length)
|
|
||||||
if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) {
|
|
||||||
val temp = strPath + File.separator + strsubdir.substring(0, i)
|
|
||||||
val subdir = File(temp)
|
|
||||||
if (!subdir.exists())
|
|
||||||
subdir.mkdir()
|
|
||||||
}
|
|
||||||
if (ignoreExistsFile && File(strtemp).exists())
|
|
||||||
continue
|
|
||||||
File(strtemp).outputStream().use({ fos ->
|
|
||||||
zipFile.copyTo(fos, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decompress the subdirectory of given zip file.
|
|
||||||
* @param zip the source zip file.
|
|
||||||
* @param dest the dest directory.
|
* @param dest the dest directory.
|
||||||
* @param subDirectory the subdirectory of the zip file to be decompressed.
|
* @param subDirectory the subdirectory of the zip file to be decompressed.
|
||||||
|
* @param callback will be called for every entry in the zip file, returns false if you dont want this file being uncompressed.
|
||||||
* @param ignoreExistentFile true if skip all existent files.
|
* @param ignoreExistentFile true if skip all existent files.
|
||||||
|
* @param allowStoredEntriesWithDataDescriptor whether the zip stream will try to read STORED entries that use a data descriptor
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExistentFile: Boolean = true) {
|
fun File.uncompressTo(dest: File, subDirectory: String = "", callback: ((String) -> Boolean)? = null, ignoreExistentFile: Boolean = true, allowStoredEntriesWithDataDescriptor: Boolean = false) {
|
||||||
val buf = ByteArray(1024)
|
val buf = ByteArray(1024)
|
||||||
dest.mkdirs()
|
dest.mkdirs()
|
||||||
ZipArchiveInputStream(zip.inputStream()).use { zipFile ->
|
ZipArchiveInputStream(this.inputStream(), null, true, allowStoredEntriesWithDataDescriptor).use { zipFile ->
|
||||||
if (zip.exists()) {
|
if (this.exists()) {
|
||||||
val strPath = dest.absolutePath
|
val strPath = dest.absolutePath
|
||||||
while (true) {
|
while (true) {
|
||||||
val zipEnt = zipFile.nextEntry ?: break
|
val zipEnt = zipFile.nextEntry ?: break
|
||||||
@@ -166,13 +119,18 @@ fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExisten
|
|||||||
gbkPath = gbkPath.substring(subDirectory.length)
|
gbkPath = gbkPath.substring(subDirectory.length)
|
||||||
if (gbkPath.startsWith("/") || gbkPath.startsWith("\\")) gbkPath = gbkPath.substring(1)
|
if (gbkPath.startsWith("/") || gbkPath.startsWith("\\")) gbkPath = gbkPath.substring(1)
|
||||||
strtemp = strPath + File.separator + gbkPath
|
strtemp = strPath + File.separator + gbkPath
|
||||||
|
|
||||||
|
if (callback != null)
|
||||||
|
if (!callback.invoke(gbkPath))
|
||||||
|
continue
|
||||||
|
|
||||||
if (zipEnt.isDirectory) {
|
if (zipEnt.isDirectory) {
|
||||||
val dir = File(strtemp)
|
val dir = File(strtemp)
|
||||||
dir.mkdirs()
|
dir.mkdirs()
|
||||||
} else {
|
} else {
|
||||||
//建目录
|
//建目录
|
||||||
val strsubdir = gbkPath
|
val strsubdir = gbkPath
|
||||||
for (i in 0..strsubdir.length - 1)
|
for (i in 0 until strsubdir.length)
|
||||||
if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) {
|
if (strsubdir.substring(i, i + 1).equals("/", ignoreCase = true)) {
|
||||||
val temp = strPath + File.separator + strsubdir.substring(0, i)
|
val temp = strPath + File.separator + strsubdir.substring(0, i)
|
||||||
val subdir = File(temp)
|
val subdir = File(temp)
|
||||||
@@ -194,14 +152,14 @@ fun unzipSubDirectory(zip: File, dest: File, subDirectory: String, ignoreExisten
|
|||||||
* Read the text content of a file in zip.
|
* Read the text content of a file in zip.
|
||||||
*
|
*
|
||||||
* @param f the zip file
|
* @param f the zip file
|
||||||
* @param location the location of the text in zip file, something like A/B/C/D.txt
|
* @param name 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.
|
* @throws IOException if the file is not a valid zip file.
|
||||||
* @return the content of given file.
|
* @return the content of given file.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun readTextFromZipFile(f: File, location: String): String {
|
fun File.readTextZipEntry(name: String): String {
|
||||||
ZipFile(f).use { zipFile ->
|
ZipFile(this).use { zipFile ->
|
||||||
val entry = zipFile.getEntry(location) ?: throw IOException("`$location` not found.")
|
val entry = zipFile.getEntry(name) ?: throw IOException("`$name` not found.")
|
||||||
return zipFile.getInputStream(entry).readFullyAsString()
|
return zipFile.getInputStream(entry).readFullyAsString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,10 +171,9 @@ fun readTextFromZipFile(f: File, location: String): String {
|
|||||||
* @param location the location of the text in zip file, something like A/B/C/D.txt
|
* @param location the location of the text in zip file, something like A/B/C/D.txt
|
||||||
* @return the content of given file.
|
* @return the content of given file.
|
||||||
*/
|
*/
|
||||||
fun readTextFromZipFileQuietly(f: File, location: String): String? {
|
fun File.readTextZipEntryQuietly(location: String) =
|
||||||
try {
|
try {
|
||||||
return readTextFromZipFile(f, location)
|
readTextZipEntry(location)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user