Skip to content
This repository has been archived by the owner on Jun 16, 2022. It is now read-only.

Commit

Permalink
add mod version pinning
Browse files Browse the repository at this point in the history
  • Loading branch information
sargunv committed Apr 16, 2020
1 parent bfe8733 commit bd7032b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 16 deletions.
34 changes: 33 additions & 1 deletion modsman-cli/src/main/kotlin/modsman/cli/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,41 @@ internal object ListCommand : CommandBase() {
@FlowPreview
override suspend fun run(jc: JCommander): Int {
RootCommand.createModsman().modlist.mods.forEach {
println("${it.projectId}: '${it.projectName}' as '${it.fileName}'")
println("${it.projectId}: '${it.projectName}' ${if (it.pinned) "pinned to" else "as"} '${it.fileName}'")
}
return 0
}
}

@Parameters(commandNames = ["pin"], commandDescription = "pin the mod to the current version; disallow upgrades")
internal object PinCommand : ProjectsCommand() {
@FlowPreview
override suspend fun run(jc: JCommander): Int {
RootCommand.createModsman().use { modsman ->
modsman.setPinnedMods(projectIds, true).collectPrintingFailures { mod ->
println("Pinned '${mod.projectName}'")
}
}
return 0
}
}

@Parameters(commandNames = ["unpin"], commandDescription = "unpin the mod to the current version; allow upgrades")
internal object UnpinCommand : ProjectsCommand() {
@FlowPreview
override suspend fun run(jc: JCommander): Int {
RootCommand.createModsman().use { modsman ->
modsman.setPinnedMods(projectIds, false).collectPrintingFailures { mod ->
println("Unpinned '${mod.projectName}'")
}
}
return 0
}
}

@Parameters(commandNames = ["unpin-all"], commandDescription = "unpin all mods in the mod list")
internal object UnpinAllCommand : AllProjectsCommand(UnpinCommand)

@Parameters(commandNames = ["list-outdated"], commandDescription = "list the mods that can be upgraded")
internal object ListOutdatedCommand : CommandBase() {
@FlowPreview
Expand Down Expand Up @@ -217,9 +246,12 @@ fun main(args: Array<String>) {
.addCommand(RemoveCommand)
.addCommand(UpgradeCommand)
.addCommand(ReinstallCommand)
.addCommand(PinCommand)
.addCommand(UnpinCommand)
.addCommand(UpgradeAllCommand)
.addCommand(RemoveAllCommand)
.addCommand(ReinstallAllCommand)
.addCommand(UnpinAllCommand)
.addCommand(DiscoverCommand)
.addCommand(ListCommand)
.addCommand(ListOutdatedCommand)
Expand Down
10 changes: 8 additions & 2 deletions modsman-core/src/main/kotlin/modsman/exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ sealed class ModsmanException : RuntimeException {
constructor(message: String, cause: Throwable) : super(message, cause)
}

class ChooseFileException(versions: List<String>) :
ModsmanException("Failed to find a valid version matching $versions")
class PinnedException(mod: ModEntry) :
ModsmanException("Won't upgrade pinned mod '${mod.projectName}'")

class ChooseFileException(config: ModlistConfig) :
ModsmanException(
"Failed to find a valid version matching ${config.requiredGameVersions} "
+ "and excluding ${config.excludedGameVersions}"
)

class UpgradeException(mod: ModEntry, cause: ChooseFileException) :
ModsmanException("Failed to upgrade '${mod.projectName}', caused by: ${cause.message}", cause)
Expand Down
3 changes: 2 additions & 1 deletion modsman-core/src/main/kotlin/modsman/modlist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ data class ModEntry(
val projectId: Int,
val projectName: String,
val fileId: Int,
val fileName: String
val fileName: String,
val pinned: Boolean = false
)

data class ModlistConfig(
Expand Down
40 changes: 28 additions & 12 deletions modsman-core/src/main/kotlin/modsman/modsman.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Modsman(
}
}
.maxBy { file -> file.fileDate }
return ret ?: throw ChooseFileException(modlist.config.requiredGameVersions)
return ret ?: throw ChooseFileException(modlist.config)
}

private fun deleteFile(mod: ModEntry): Boolean {
Expand All @@ -63,7 +63,7 @@ class Modsman(
private suspend fun fingerprint(jarPath: Path): Long {
val whitespace = setOf<Byte>(9, 10, 13, 32)
val data = io { readToBytes(jarPath) }
.filter { it !in whitespace }.toByteArray()
.filter { byte -> byte !in whitespace }.toByteArray()
return Murmur2.hash(data, data.size, 1)
}

Expand Down Expand Up @@ -113,42 +113,58 @@ class Modsman(
suspend fun addMods(projectIds: List<Int>): Flow<Result<ModEntry>> {
return io {
curseforgeClient.getAddonsAsync(projectIds).await()
}.parallelMapToResultFlow(downloadPool) {
installMod(it.addonId, it.name)
}.parallelMapToResultFlow(downloadPool) { cfAddon ->
installMod(cfAddon.addonId, cfAddon.name)
}
}

@FlowPreview
fun setPinnedMods(projectIds: List<Int>, pinned: Boolean): Flow<Result<ModEntry>> {
return projectIds
.mapNotNull(modlist::get)
.toFlow { mod ->
modlist.addOrUpdate(mod.copy(pinned = pinned))
Result.success(mod)
}
}

@FlowPreview
suspend fun removeMods(projectIds: List<Int>): Flow<Result<ModEntry>> {
return projectIds
.mapNotNull(modlist::get)
.parallelMapToResultFlow(downloadPool) {
io { deleteFile(it) }
modlist.remove(it.projectId)
.parallelMapToResultFlow(downloadPool) { mod ->
io { deleteFile(mod) }
modlist.remove(mod.projectId)
}
.filterNot { it.exceptionOrNull() is ProjectNotFoundException }
.filterNot { result -> result.exceptionOrNull() is ProjectNotFoundException }
}

@FlowPreview
suspend fun upgradeMods(projectIds: List<Int>): Flow<Result<Pair<ModEntry, ModEntry>>> {
return projectIds
.mapNotNull(modlist::get)
.parallelMapToResultFlow(downloadPool) { mod -> mod to upgradeMod(mod) }
.filter { it.map { (old, new) -> old != new }.getOrElse { true } }
.parallelMapToResultFlow(downloadPool) { mod ->
if (mod.pinned)
throw PinnedException(mod)
mod to upgradeMod(mod)
}
.filter { result -> result.map { (old, new) -> old != new }.getOrElse { true } }
}

@FlowPreview
suspend fun getOutdatedMods(): Flow<Result<Pair<ModEntry, String>>> {
return modlist.mods
.parallelMapToResultFlow(downloadPool) { mod ->
if (mod.pinned)
throw PinnedException(mod)
try {
mod to getBestFile(mod.projectId)
} catch (e: ChooseFileException) {
throw UpgradeException(mod, e)
}
}
.filter { it.map { (mod, file) -> mod.fileId != file.fileId }.getOrElse { true } }
.map { it.map { (mod, file) -> mod to file.fileName } }
.filter { result -> result.map { (mod, file) -> mod.fileId != file.fileId }.getOrElse { true } }
.map { result -> result.map { (mod, file) -> mod to file.fileName } }
}

@FlowPreview
Expand Down

0 comments on commit bd7032b

Please sign in to comment.