Compare commits

..

4 Commits

2 changed files with 68 additions and 43 deletions

View File

@@ -180,17 +180,19 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, de
fun download(packFolder: String, indexUri: SpaceSafeURI) { fun download(packFolder: String, indexUri: SpaceSafeURI) {
if (err != null) return if (err != null) return
// Ensure wrong-side or optional false files are removed // Exclude wrong-side and optional false files
cachedFile?.let { cachedFile?.let {
if (!it.optionValue || !correctSide()) { if ((it.isOptional && !it.optionValue) || !correctSide()) {
if (it.cachedLocation == null) return if (it.cachedLocation != null) {
// Ensure wrong-side or optional false files are removed
try { try {
Files.deleteIfExists(Paths.get(packFolder, it.cachedLocation)) Files.deleteIfExists(Paths.get(packFolder, it.cachedLocation))
} catch (e: IOException) { } catch (e: IOException) {
Log.warn("Failed to delete file before downloading", e) Log.warn("Failed to delete file", e)
}
} }
it.cachedLocation = null it.cachedLocation = null
return
} }
} }
if (alreadyUpToDate) return if (alreadyUpToDate) return
@@ -292,7 +294,7 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, de
companion object { companion object {
@JvmStatic @JvmStatic
fun createTasksFromIndex(index: IndexFile, defaultFormat: String, downloadSide: Side): List<DownloadTask> { fun createTasksFromIndex(index: IndexFile, defaultFormat: String, downloadSide: Side): MutableList<DownloadTask> {
val tasks = ArrayList<DownloadTask>() val tasks = ArrayList<DownloadTask>()
for (file in Objects.requireNonNull(index.files)) { for (file in Objects.requireNonNull(index.files)) {
tasks.add(DownloadTask(file, defaultFormat, downloadSide)) tasks.add(DownloadTask(file, defaultFormat, downloadSide))

View File

@@ -128,7 +128,9 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
if (manifest.packFileHash?.let { packFileSource.hashIsEqual(it) } == true && invalidatedUris.isEmpty()) { if (manifest.packFileHash?.let { packFileSource.hashIsEqual(it) } == true && invalidatedUris.isEmpty()) {
// todo: --force? // todo: --force?
ui.submitProgress(InstallProgress("Modpack is already up to date!", 1, 1)) ui.submitProgress(InstallProgress("Modpack is already up to date!", 1, 1))
ui.awaitOptionalButton(false) if (manifest.cachedFiles.any { it.value.isOptional }) {
ui.awaitOptionalButton(false)
}
if (!ui.optionsButtonPressed) { if (!ui.optionsButtonPressed) {
return return
} }
@@ -186,7 +188,9 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
private fun processIndex(indexUri: SpaceSafeURI, indexHash: Hash, hashFormat: String, manifest: ManifestFile, invalidatedUris: List<SpaceSafeURI>) { private fun processIndex(indexUri: SpaceSafeURI, indexHash: Hash, hashFormat: String, manifest: ManifestFile, invalidatedUris: List<SpaceSafeURI>) {
if (manifest.indexFileHash == indexHash && invalidatedUris.isEmpty()) { if (manifest.indexFileHash == indexHash && invalidatedUris.isEmpty()) {
ui.submitProgress(InstallProgress("Modpack files are already up to date!", 1, 1)) ui.submitProgress(InstallProgress("Modpack files are already up to date!", 1, 1))
ui.awaitOptionalButton(false) if (manifest.cachedFiles.any { it.value.isOptional }) {
ui.awaitOptionalButton(false)
}
if (!ui.optionsButtonPressed) { if (!ui.optionsButtonPressed) {
return return
} }
@@ -310,8 +314,8 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
} }
// TODO: task failed function? // TODO: task failed function?
val nonFailedFirstTasks = tasks.filter { t -> !t.failed() }.toList() tasks.removeAll { it.failed() }
val optionTasks = nonFailedFirstTasks.filter(DownloadTask::correctSide).filter(DownloadTask::isOptional).toList() val optionTasks = tasks.filter(DownloadTask::correctSide).filter(DownloadTask::isOptional).toList()
val optionsChanged = optionTasks.any(DownloadTask::isNewOptional) val optionsChanged = optionTasks.any(DownloadTask::isNewOptional)
if (optionTasks.isNotEmpty() && !optionsChanged) { if (optionTasks.isNotEmpty() && !optionsChanged) {
if (!ui.optionsButtonPressed) { if (!ui.optionsButtonPressed) {
@@ -335,35 +339,11 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
// TODO: keep this enabled? then apply changes after download process? // TODO: keep this enabled? then apply changes after download process?
ui.disableOptionsButton(optionTasks.isNotEmpty()) ui.disableOptionsButton(optionTasks.isNotEmpty())
ui.submitProgress(InstallProgress("Validating existing files...")) while (true) {
when (validateAndResolve(tasks)) {
// Validate existing files ResolveResult.RETRY -> {}
for (downloadTask in nonFailedFirstTasks.filter(DownloadTask::correctSide)) { ResolveResult.QUIT -> return
downloadTask.validateExistingFile(opts.packFolder) ResolveResult.SUCCESS -> break
}
// Resolve CurseForge metadata
val cfFiles = nonFailedFirstTasks.asSequence().filter { !it.alreadyUpToDate }
.filter(DownloadTask::correctSide)
.map { it.metadata }
.filter { it.linkedFile != null }
.filter { it.linkedFile?.download?.mode == "metadata:curseforge" }.toList()
if (cfFiles.isNotEmpty()) {
ui.submitProgress(InstallProgress("Resolving CurseForge metadata..."))
val resolveFailures = resolveCfMetadata(cfFiles)
if (resolveFailures.isNotEmpty()) {
errorsOccurred = true
when (ui.showExceptions(resolveFailures, cfFiles.size, true)) {
ExceptionListResult.CONTINUE -> {}
ExceptionListResult.CANCEL -> {
cancelled = true
return
}
ExceptionListResult.IGNORE -> {
cancelledStartGame = true
return
}
}
} }
} }
@@ -414,7 +394,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
// Shut down the thread pool when the update is done // Shut down the thread pool when the update is done
threadPool.shutdown() threadPool.shutdown()
val failedTasks2ElectricBoogaloo = nonFailedFirstTasks.asSequence().map(DownloadTask::exceptionDetails).filterNotNull().toList() val failedTasks2ElectricBoogaloo = tasks.asSequence().map(DownloadTask::exceptionDetails).filterNotNull().toList()
if (failedTasks2ElectricBoogaloo.isNotEmpty()) { if (failedTasks2ElectricBoogaloo.isNotEmpty()) {
errorsOccurred = true errorsOccurred = true
when (ui.showExceptions(failedTasks2ElectricBoogaloo, tasks.size, false)) { when (ui.showExceptions(failedTasks2ElectricBoogaloo, tasks.size, false)) {
@@ -425,6 +405,49 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse
} }
} }
enum class ResolveResult {
RETRY,
QUIT,
SUCCESS;
}
private fun validateAndResolve(nonFailedFirstTasks: List<DownloadTask>): ResolveResult {
ui.submitProgress(InstallProgress("Validating existing files..."))
// Validate existing files
for (downloadTask in nonFailedFirstTasks.filter(DownloadTask::correctSide)) {
downloadTask.validateExistingFile(opts.packFolder)
}
// Resolve CurseForge metadata
val cfFiles = nonFailedFirstTasks.asSequence().filter { !it.alreadyUpToDate }
.filter(DownloadTask::correctSide)
.map { it.metadata }
.filter { it.linkedFile != null }
.filter { it.linkedFile?.download?.mode == "metadata:curseforge" }.toList()
if (cfFiles.isNotEmpty()) {
ui.submitProgress(InstallProgress("Resolving CurseForge metadata..."))
val resolveFailures = resolveCfMetadata(cfFiles)
if (resolveFailures.isNotEmpty()) {
errorsOccurred = true
return when (ui.showExceptions(resolveFailures, cfFiles.size, true)) {
ExceptionListResult.CONTINUE -> {
ResolveResult.RETRY
}
ExceptionListResult.CANCEL -> {
cancelled = true
ResolveResult.QUIT
}
ExceptionListResult.IGNORE -> {
cancelledStartGame = true
ResolveResult.QUIT
}
}
}
}
return ResolveResult.SUCCESS
}
private fun showCancellationDialog() { private fun showCancellationDialog() {
when (ui.showCancellationDialog()) { when (ui.showCancellationDialog()) {
CancellationResult.QUIT -> cancelled = true CancellationResult.QUIT -> cancelled = true