From bbc3c08a0dacd42ad9c19f56ddfeb8ff4221903c Mon Sep 17 00:00:00 2001 From: nikky Date: Sun, 31 Jan 2021 00:56:28 +0100 Subject: [PATCH] add curse importing --- .github/workflows/PUBLISH_SNAPSHOT.yml | 13 +- .../src/main/kotlin/voodoo/builder/Resolve.kt | 2 +- .../main/kotlin/voodoo/curse/CurseClient.kt | 15 +- .../kotlin/voodoo/provider/CurseProvider.kt | 6 +- .../kotlin/voodoo/provider/DirectProvider.kt | 4 +- .../kotlin/voodoo/provider/JenkinsProvider.kt | 4 +- .../kotlin/voodoo/provider/LocalProvider.kt | 4 +- .../kotlin/voodoo/provider/NoopProvider.kt | 4 +- .../kotlin/voodoo/provider/ProviderBase.kt | 8 +- samples/config.json5 | 37 +-- samples/fabricpack/152Version.voodoo.json5 | 20 +- samples/fabricpack/164Version.voodoo.json5 | 22 +- samples/fabricpack/lock/0.0.3/lock.pack.json | 2 +- samples/fabricpack/old_0.0.1.voodoo.json5 | 18 +- samples/forgePack/v0.0.1.voodoo.json5 | 4 +- .../kotlin/voodoo/cli/ImportCurseCommand.kt | 256 ++++++++++++++++++ .../main/kotlin/voodoo/cli/VoodooCommand.kt | 3 +- .../kotlin/voodoo/config/Configuration.kt | 20 +- .../main/kotlin/voodoo/config/Extensions.kt | 34 +-- 19 files changed, 369 insertions(+), 107 deletions(-) create mode 100644 voodoo/src/main/kotlin/voodoo/cli/ImportCurseCommand.kt diff --git a/.github/workflows/PUBLISH_SNAPSHOT.yml b/.github/workflows/PUBLISH_SNAPSHOT.yml index 5927feec..8c74caf8 100644 --- a/.github/workflows/PUBLISH_SNAPSHOT.yml +++ b/.github/workflows/PUBLISH_SNAPSHOT.yml @@ -59,4 +59,15 @@ jobs: voodoo-${{ env.VERSION }}.jar:voodoo/build/libs/voodoo-${{ env.VERSION }}-all.jar multimc-installer-${{ env.VERSION }}.jar:multimc/installer/build/libs/multimc-installer-${{ env.VERSION }}-all.jar server-installer-${{ env.VERSION }}.jar:server-installer/build/libs/server-installer-${{ env.VERSION }}-all.jar - empty_voodoo_project.zip:empty_voodoo_project.zip \ No newline at end of file + empty_voodoo_project.zip:empty_voodoo_project.zip + - name: Send Webhook Notification + if: always() + env: + JOB_STATUS: ${{ job.status }} + WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} + HOOK_OS_NAME: ${{ runner.os }} + WORKFLOW_NAME: ${{ github.workflow }} + run: | + git clone https://github.com/DiscordHooks/github-actions-discord-webhook.git webhook + bash webhook/send.sh $JOB_STATUS $WEBHOOK_URL + shell: bash \ No newline at end of file diff --git a/core/src/main/kotlin/voodoo/builder/Resolve.kt b/core/src/main/kotlin/voodoo/builder/Resolve.kt index a5f6e660..849ee524 100644 --- a/core/src/main/kotlin/voodoo/builder/Resolve.kt +++ b/core/src/main/kotlin/voodoo/builder/Resolve.kt @@ -108,7 +108,7 @@ suspend fun resolve( val provider = voodoo.provider.Providers.forEntry(entry)!! entry.takeUnless { it is FlatEntry.Noop }?.let { entry -> - val lockEntry = provider.resolve(entry, modPack.mcVersion, addEntries) + val lockEntry = provider.resolve(entry, modPack, addEntries) logger.debug("received locked entry: $lockEntry") logger.debug("validating: $lockEntry") diff --git a/core/src/main/kotlin/voodoo/curse/CurseClient.kt b/core/src/main/kotlin/voodoo/curse/CurseClient.kt index 9bfa9a70..a7614910 100644 --- a/core/src/main/kotlin/voodoo/curse/CurseClient.kt +++ b/core/src/main/kotlin/voodoo/curse/CurseClient.kt @@ -19,6 +19,7 @@ import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json import mu.KLogging +import voodoo.data.ModloaderPattern import voodoo.data.curse.Addon import voodoo.data.curse.AddonFile import voodoo.data.curse.FileID @@ -96,7 +97,7 @@ object CurseClient : KLogging() { slug?.let { filters += "slug: \"$it\"" } - logger.info("post $url $filters") + logger.info { "post $url $filters" } val requestBody = GraphQLRequest( query = """{ | addons(${filters.joinToString(", ")}) { @@ -266,7 +267,7 @@ object CurseClient : KLogging() { private val getAddonCache: MutableMap = HashMap(1 shl 0) suspend fun getAddon(addonId: ProjectID, fail: Boolean = true): Addon? { - if (!addonId.valid) throw IllegalStateException("invalid project id") + if (!addonId.valid) throw IllegalStateException("invalid project id: $addonId") return getAddonCache.getOrPut(addonId) { getAddonCall(addonId, fail) } } @@ -408,7 +409,8 @@ object CurseClient : KLogging() { suspend fun findFile( entry: FlatEntry.Curse, - mcVersion: String + mcVersion: String, + modloader: ModloaderPattern? ): Triple { val mcVersions = listOf(mcVersion) + entry.validMcVersions val slug = entry.id // TODO: maybe make into separate property @@ -417,6 +419,13 @@ object CurseClient : KLogging() { var addonId = entry.projectID val fileNameRegex = entry.fileNameRegex + if(modloader is ModloaderPattern.Forge) { + entry.invalidMcVersions += "Fabric" + } + if(modloader is ModloaderPattern.Fabric) { + entry.invalidMcVersions += "Forge" + } + val addon = if (!addonId.valid) { // slug.takeUnless { it.isBlank() } // ?.let { getAddonBySlug(it) } diff --git a/core/src/main/kotlin/voodoo/provider/CurseProvider.kt b/core/src/main/kotlin/voodoo/provider/CurseProvider.kt index 0fc9f93b..ddb7858f 100644 --- a/core/src/main/kotlin/voodoo/provider/CurseProvider.kt +++ b/core/src/main/kotlin/voodoo/provider/CurseProvider.kt @@ -2,7 +2,6 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.runBlocking import voodoo.curse.CurseClient import voodoo.curse.CurseClient.findFile @@ -16,6 +15,7 @@ import voodoo.data.curse.CurseDependencyType import voodoo.data.curse.FileID import voodoo.data.curse.ProjectID import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import voodoo.memoize import voodoo.util.download @@ -38,11 +38,11 @@ object CurseProvider : ProviderBase("Curse Provider") { override suspend fun resolve( entry: FlatEntry, - mcVersion: String, + modPack: FlatModPack, addEntry: SendChannel ): LockEntry { entry as FlatEntry.Curse - val (projectID, fileID, path) = findFile(entry, mcVersion) + val (projectID, fileID, path) = findFile(entry, modPack.mcVersion, modPack.modloader) // synchronized(resolved) { // logger.info("resolved: ${resolved.count()} unique entries") diff --git a/core/src/main/kotlin/voodoo/provider/DirectProvider.kt b/core/src/main/kotlin/voodoo/provider/DirectProvider.kt index 39e95e5c..0c6757a4 100644 --- a/core/src/main/kotlin/voodoo/provider/DirectProvider.kt +++ b/core/src/main/kotlin/voodoo/provider/DirectProvider.kt @@ -2,9 +2,9 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.FlowCollector import voodoo.data.EntryReportData import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import voodoo.util.download import java.io.File @@ -18,7 +18,7 @@ import java.net.URL object DirectProvider : ProviderBase("Direct Provider") { override suspend fun resolve( entry: FlatEntry, - mcVersion: String, + modPack: FlatModPack, addEntry: SendChannel ): LockEntry { entry as FlatEntry.Direct diff --git a/core/src/main/kotlin/voodoo/provider/JenkinsProvider.kt b/core/src/main/kotlin/voodoo/provider/JenkinsProvider.kt index 36a4a119..69489b86 100644 --- a/core/src/main/kotlin/voodoo/provider/JenkinsProvider.kt +++ b/core/src/main/kotlin/voodoo/provider/JenkinsProvider.kt @@ -3,12 +3,12 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.runBlocking import voodoo.core.GeneratedConstants import voodoo.data.EntryReportData import voodoo.data.Quadruple import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import voodoo.memoize import voodoo.util.download @@ -30,7 +30,7 @@ object JenkinsProvider : ProviderBase("Jenkins Provider") { override suspend fun resolve( entry: FlatEntry, - mcVersion: String, + modPack: FlatModPack, addEntry: SendChannel ): LockEntry { entry as FlatEntry.Jenkins diff --git a/core/src/main/kotlin/voodoo/provider/LocalProvider.kt b/core/src/main/kotlin/voodoo/provider/LocalProvider.kt index aa77f1c0..e792cfe4 100644 --- a/core/src/main/kotlin/voodoo/provider/LocalProvider.kt +++ b/core/src/main/kotlin/voodoo/provider/LocalProvider.kt @@ -2,9 +2,9 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.FlowCollector import voodoo.data.EntryReportData import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import java.io.File @@ -16,7 +16,7 @@ import java.io.File object LocalProvider : ProviderBase("Local Provider") { override suspend fun resolve( entry: FlatEntry, - mcVersion: String, + modPack: FlatModPack, addEntry: SendChannel ): LockEntry { entry as FlatEntry.Local diff --git a/core/src/main/kotlin/voodoo/provider/NoopProvider.kt b/core/src/main/kotlin/voodoo/provider/NoopProvider.kt index 522d2a59..8add5d30 100644 --- a/core/src/main/kotlin/voodoo/provider/NoopProvider.kt +++ b/core/src/main/kotlin/voodoo/provider/NoopProvider.kt @@ -2,9 +2,9 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.FlowCollector import voodoo.data.EntryReportData import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import java.io.File @@ -16,7 +16,7 @@ import java.io.File object NoopProvider : ProviderBase("Noop Provider") { override suspend fun resolve( entry: FlatEntry, - mcVersion: String, + modPack: FlatModPack, addEntry: SendChannel ): LockEntry { entry as FlatEntry.Noop diff --git a/core/src/main/kotlin/voodoo/provider/ProviderBase.kt b/core/src/main/kotlin/voodoo/provider/ProviderBase.kt index d89a0a45..b461b367 100644 --- a/core/src/main/kotlin/voodoo/provider/ProviderBase.kt +++ b/core/src/main/kotlin/voodoo/provider/ProviderBase.kt @@ -2,11 +2,11 @@ package voodoo.provider import com.eyeem.watchadoin.Stopwatch import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.FlowCollector import mu.KLogging import voodoo.data.DependencyType import voodoo.data.EntryReportData import voodoo.data.flat.FlatEntry +import voodoo.data.flat.FlatModPack import voodoo.data.lock.LockEntry import java.io.File import java.time.Instant @@ -26,7 +26,11 @@ abstract class ProviderBase( open fun reset() {} - open suspend fun resolve(entry: FlatEntry, mcVersion: String, addEntry: SendChannel): LockEntry { + open suspend fun resolve( + entry: FlatEntry, + modPack: FlatModPack, + addEntry: SendChannel + ): LockEntry { logger.info("[$name] resolve ${entry.id}") throw NotImplementedError("unable to resolve") } diff --git a/samples/config.json5 b/samples/config.json5 index ed6d66ab..e3f0c5f8 100644 --- a/samples/config.json5 +++ b/samples/config.json5 @@ -16,50 +16,17 @@ "section": "RESOURCE_PACKS", "mcVersions": [] }, - "Fabric15": { - "section": "MODS", - "categories": [ - "Fabric" - ], - "mcVersions": [ - "1.15", - "1.15.1", - "1.15.2" - ] - }, - "Fabric16": { + "Fabric": { "section": "MODS", "categories": [ "Fabric" ], "mcVersions": [ - "1.16", - "1.16.1", - "1.16.2", - "1.16.3", - "1.16.4" ] } }, "forgeGenerators": { - "Forge": { - "mcVersions": [ - "1.12.2", - "1.15.2", - "1.16.4", - "1.16.5" - ] - }, - "Forge_12_2": { - "mcVersions": [ - "1.12.2" - ] - }, - "Forge_15_2": { - "mcVersions": [ - "1.15.2" - ] - } + "Forge": {}, }, "fabricGenerators": { "Fabric": { diff --git a/samples/fabricpack/152Version.voodoo.json5 b/samples/fabricpack/152Version.voodoo.json5 index ab11f233..29c0eec9 100644 --- a/samples/fabricpack/152Version.voodoo.json5 +++ b/samples/fabricpack/152Version.voodoo.json5 @@ -10,40 +10,40 @@ "mods": [ { "type": "curse", - "projectName": "Fabric15/fabric-api" + "projectName": "Fabric/fabric-api" }, { "type": "curse", - "projectName": "Fabric15/betternether" + "projectName": "Fabric/betternether" }, { "type": "curse", - "projectName": "Fabric15/tab-inventory-fabric" + "projectName": "Fabric/tab-inventory-fabric" }, { "type": "curse", - "projectName": "Fabric15/campanion" + "projectName": "Fabric/campanion" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/roughly-enough-items" + "projectName": "Fabric/roughly-enough-items" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/roughly-enough-resources" + "projectName": "Fabric/roughly-enough-resources" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/modmenu" + "projectName": "Fabric/modmenu" }, { "type": "curse", @@ -51,7 +51,7 @@ "client", "optionalStarred" ], - "projectName": "Fabric15/mouse-wheelie" + "projectName": "Fabric/mouse-wheelie" }, { "type": "curse", @@ -59,11 +59,11 @@ "client", "optionalStarred" ], - "projectName": "Fabric15/appleskin" + "projectName": "Fabric/appleskin" }, { "type": "curse", - "projectName": "Fabric15/hwyla" + "projectName": "Fabric/hwyla" } ] } \ No newline at end of file diff --git a/samples/fabricpack/164Version.voodoo.json5 b/samples/fabricpack/164Version.voodoo.json5 index 6064e55a..5f433ced 100644 --- a/samples/fabricpack/164Version.voodoo.json5 +++ b/samples/fabricpack/164Version.voodoo.json5 @@ -8,27 +8,27 @@ "intermediateMappings": "Fabric/1.16.4" }, "mods": [ - "curse=Fabric16/fabric-api", - "curse=Fabric16/betternether", - "curse=Fabric16/betternether", + "curse=Fabric/fabric-api", + "curse=Fabric/betternether", + "curse=Fabric/betternether", // comment { "type": "curse", - "projectName": "Fabric16/tab-inventory-fabric", + "projectName": "Fabric/tab-inventory-fabric", "validMcVersions": ["1.16", "1.16.1", "1.16.2", "1.16.3"] }, { "type": "curse", - "projectName": "Fabric16/campanion", + "projectName": "Fabric/campanion", }, - "curse:client=Fabric16/roughly-enough-items", - "curse:client=Fabric16/roughly-enough-resources", - "curse:client=Fabric16/modmenu", - "curse:client,optionalStarred=Fabric16/mouse-wheelie", - "curse:client,optionalStarred=Fabric16/appleskin", + "curse:client=Fabric/roughly-enough-items", + "curse:client=Fabric/roughly-enough-resources", + "curse:client=Fabric/modmenu", + "curse:client,optionalStarred=Fabric/mouse-wheelie", + "curse:client,optionalStarred=Fabric/appleskin", { "type": "curse", - "projectName": "Fabric16/hwyla", + "projectName": "Fabric/hwyla", "validMcVersions": ["1.16.3"] }, { diff --git a/samples/fabricpack/lock/0.0.3/lock.pack.json b/samples/fabricpack/lock/0.0.3/lock.pack.json index ba4e8ab5..e2242bc1 100644 --- a/samples/fabricpack/lock/0.0.3/lock.pack.json +++ b/samples/fabricpack/lock/0.0.3/lock.pack.json @@ -104,7 +104,7 @@ "fabric-api": "REQUIRED" }, "projectID": 310111, - "fileID": 3175580 + "fileID": 3183996 }, { "type": "curse", diff --git a/samples/fabricpack/old_0.0.1.voodoo.json5 b/samples/fabricpack/old_0.0.1.voodoo.json5 index 27050502..103167fe 100644 --- a/samples/fabricpack/old_0.0.1.voodoo.json5 +++ b/samples/fabricpack/old_0.0.1.voodoo.json5 @@ -10,40 +10,40 @@ "mods": [ { "type": "curse", - "projectName": "Fabric15/fabric-api" + "projectName": "Fabric/fabric-api" }, { "type": "curse", - "projectName": "Fabric15/betternether" + "projectName": "Fabric/betternether" }, { "type": "curse", - "projectName": "Fabric15/tab-inventory-fabric" + "projectName": "Fabric/tab-inventory-fabric" }, { "type": "curse", - "projectName": "Fabric15/campanion" + "projectName": "Fabric/campanion" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/roughly-enough-items" + "projectName": "Fabric/roughly-enough-items" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/roughly-enough-resources" + "projectName": "Fabric/roughly-enough-resources" }, { "type": "curse", "applyOverrides": [ "client" ], - "projectName": "Fabric15/modmenu" + "projectName": "Fabric/modmenu" }, { "type": "curse", @@ -51,7 +51,7 @@ "client", "optionalStarred" ], - "projectName": "Fabric15/mouse-wheelie" + "projectName": "Fabric/mouse-wheelie" }, { "type": "curse", @@ -59,7 +59,7 @@ "client", "optionalStarred" ], - "projectName": "Fabric15/appleskin" + "projectName": "Fabric/appleskin" } ] } \ No newline at end of file diff --git a/samples/forgePack/v0.0.1.voodoo.json5 b/samples/forgePack/v0.0.1.voodoo.json5 index f050ad87..0990bef9 100644 --- a/samples/forgePack/v0.0.1.voodoo.json5 +++ b/samples/forgePack/v0.0.1.voodoo.json5 @@ -5,12 +5,12 @@ "mcVersion": "1.12.2", "modloader": { "type": "modloader.forge", - "version": "Forge_12_2/1.12.2/14.23.5.2854" + "version": "Forge/1.12.2/14.23.5.2854" }, "mods": [ { "type": "curse", - "projectName": "Forge/foamfix-optimization-mod" + "projectName": "Mod/foamfix-optimization-mod" } ] } \ No newline at end of file diff --git a/voodoo/src/main/kotlin/voodoo/cli/ImportCurseCommand.kt b/voodoo/src/main/kotlin/voodoo/cli/ImportCurseCommand.kt new file mode 100644 index 00000000..04fefec1 --- /dev/null +++ b/voodoo/src/main/kotlin/voodoo/cli/ImportCurseCommand.kt @@ -0,0 +1,256 @@ +package voodoo.cli + +import com.eyeem.watchadoin.Stopwatch +import com.eyeem.watchadoin.saveAsHtml +import com.eyeem.watchadoin.saveAsSvg +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.requireObject +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.arguments.multiple +import com.github.ajalt.clikt.parameters.arguments.validate +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import com.github.ajalt.clikt.parameters.options.validate +import com.github.ajalt.clikt.parameters.types.file +import kotlinx.coroutines.* +import kotlinx.coroutines.slf4j.MDCContext +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.Json +import mu.KotlinLogging +import mu.withLoggingContext +import voodoo.builder.Builder +import voodoo.config.Autocompletions +import voodoo.config.Configuration +import voodoo.curse.CurseClient +import voodoo.data.components.CurseComponent +import voodoo.data.curse.CurseFile +import voodoo.data.curse.CurseManifest +import voodoo.data.curse.FileType +import voodoo.data.curse.ProjectID +import voodoo.pack.* +import voodoo.poet.generator.CurseSection +import voodoo.util.* +import java.io.File + +class ImportCurseCommand() : CliktCommand( + name = "importCurse", + help = "imports a pack from curse" +) { + private val logger = KotlinLogging.logger {} + + val cliContext by requireObject() + + val id by option( + "--id", + help = "pack id" + ).required() + .validate { + require(it.isNotBlank()) { "id must not be blank" } + require(it.matches("""[\w_]+""".toRegex())) { "modpack id must not contain special characters" } + } + + val pinFiles by option( + "--pinFiles", + help = "pin file versions" + ).flag(default = false) + + val uri: String by argument("CURSE_PACK") + + override fun run() = withLoggingContext("command" to commandName) { + val directories = Directories.get(moduleName = "CURSE") + val cacheHome by lazy { + directories.cacheHome.resolve(id).apply { + mkdirs() + } + } + + runBlocking(MDCContext()) { + val stopwatch = Stopwatch(commandName) + + val rootDir = cliContext.rootDir + + val jsonPretty = Json(json) { + prettyPrint = true + encodeDefaults = false + } + + stopwatch { + val importFile = cacheHome.resolve("file.zip") + // TODO: download or load from file + if(uri.startsWith("http")) { + importFile.download(uri, cacheDir = null) + } else { + File(uri).copyTo(importFile) + } + + logger.info { "downloaded: $importFile" } + + // TODO: unzip + val importFolder = cacheHome.resolve("unzipped") + importFolder.deleteRecursively() + UnzipUtility.unzip(importFile, importFolder) + + // TODO: parse curse manifest + val manifest = json.decodeFromString(CurseManifest.serializer(), importFolder.resolve("manifest.json").readText()) + + val iconFile = importFolder.resolve(manifest.overrides).resolve("icon.png") + +// val modpackAddon = CurseClient.getAddon(ProjectID(manifest.projectID)) ?: error("cannot load addon modpack") + + logger.info { jsonPretty.encodeToString(CurseManifest.serializer(), manifest) } + + val baseDir = rootDir.resolve(id) + + // convert to meta pack + val metaPackFile = baseDir.resolve(MetaPack.FILENAME) + metaPackFile.absoluteFile.parentFile.mkdirs() + val metaPack = MetaPack( + title = manifest.name, + authors = listOf(manifest.author).filter { it.isNotBlank() }, +// authors = modpackAddon.authors.map { author -> +// author.name +// }, +// icon = "icon.png", + uploadBaseUrl = "https://mydomain.com/mc/", + ) + + + // convert to version pack + val version = manifest.version + val cleanVersion = version //.replace(".", "_") // TODO: cleanup of invalid chars + val srcFolder = baseDir.resolve("src_$cleanVersion") + + val versionPackFile = baseDir.resolve("${cleanVersion}.voodoo.json5") + + // ensure no version conflict is created + if(versionPackFile.exists()) { + error("version already exists: $versionPackFile") + } + if(srcFolder.exists() && srcFolder.list()?.isEmpty() == false) { + error("folder $srcFolder already exists") + } + + val curseModloader = manifest.minecraft.modLoaders.first { it.primary == true } + val modloader = if(curseModloader.id.startsWith("forge-")) { + val forgeVersion = manifest.minecraft.version + "-" + curseModloader.id.substringAfter("forge-") + val forgeAlias = Autocompletions.forge.entries.firstOrNull { (key, value) -> + value == forgeVersion + }?.key ?: run { + logger.error { "cannot find forge alias for $forgeVersion in autocompletions, make sure to add it in ${Configuration.CONFIG_PATH}" } + forgeVersion + } + Modloader.Forge(version = forgeAlias) + } else { + error("unknown modloader $curseModloader") + } + + val mods = withPool { pool -> + manifest.files.map { curseFileEntry: CurseFile -> + async(CoroutineName("processing_${curseFileEntry.projectID}") + pool) { + logger.info { "processing: $curseFileEntry" } + + // get curseforge info + val addon = CurseClient.getAddon(addonId = curseFileEntry.projectID) ?: error("cannot get addon") + val addonFile = CurseClient.getAddonFile(addonId = curseFileEntry.projectID, fileId = curseFileEntry.fileID) ?: error("cannot get addon file") + + val projectName = Autocompletions.curseforge.entries.firstOrNull { (key, projectIdString) -> + projectIdString.toInt() == curseFileEntry.projectID.value + }?.key ?: run { + logger.error { "cannot find $curseFileEntry in autocompletions, make sure to add it in ${Configuration.CONFIG_PATH}" } + "add_me/" + addon.slug + } + var hasCustomProperties = false + + var entry = FileEntry.Curse( + projectName = projectName, + ) + +// // TODO: handle this properly +// entry.invalidMcVersions += "Fabric" +// hasCustomProperties = true + + if(pinFiles) { + entry = entry.copy( + curse = entry.curse.copy( + fileID = curseFileEntry.fileID + ) + ) + hasCustomProperties = true + } + + if(addonFile.releaseType != FileType.Release) { + entry = entry.copy( + curse = entry.curse.copy( + releaseTypes = entry.curse.releaseTypes + addonFile.releaseType + ) + ) + hasCustomProperties = true + } + if(!addonFile.gameVersion.contains(manifest.minecraft.version)) { + entry.validMcVersions = addonFile.gameVersion.toSet() - "Forge" + hasCustomProperties = true + } + when (addon.categorySection.name) { + CurseSection.MODS.sectionName -> { +// folder = "mods" + } + CurseSection.RESOURCE_PACKS.sectionName -> { + entry.folder = "resourcepacks" + hasCustomProperties = true + } + else -> { + error("unknown category type: ${addon.categorySection}") + } + } +// fileNameRegex = "\\Q${addonFile.fileName}\\E" + + if(!hasCustomProperties) { + val entryString = "curse=${entry.projectName}" + jsonPretty.encodeToJsonElement( + String.serializer(), + entryString + ) + } else { + jsonPretty.encodeToJsonElement( + FileEntry.serializer(), + entry + ) + } + } + }.awaitAll() + } + + val targetIconFile = srcFolder.resolve("icon.png") + + val versionPack = VersionPack( + title = manifest.name + " v" + version, + icon = if(iconFile.exists()) targetIconFile.toRelativeUnixPath(baseDir) else null, + version = version, + srcDir = srcFolder.toRelativeUnixPath(baseDir), + mcVersion = manifest.minecraft.version, + modloader = modloader, + mods = mods + ).postParse(baseDir = baseDir) + + if(!metaPackFile.exists()) { + metaPackFile.writeText( + jsonPretty.encodeToString(MetaPack.serializer(), metaPack) + ) + } + + versionPackFile.writeText( + jsonPretty.encodeToString(VersionPack.serializer(), versionPack) + ) + + // copy configs into src and local + srcFolder.mkdirs() + importFolder.resolve(manifest.overrides).copyRecursively(srcFolder) + } + + val reportDir = rootDir.resolve("reports").apply { mkdirs() } + stopwatch.saveAsSvg(reportDir.resolve("importCurse.report.svg")) + stopwatch.saveAsHtml(reportDir.resolve("importCurse.report.html")) + } + } +} \ No newline at end of file diff --git a/voodoo/src/main/kotlin/voodoo/cli/VoodooCommand.kt b/voodoo/src/main/kotlin/voodoo/cli/VoodooCommand.kt index 1d80b342..0d22446d 100644 --- a/voodoo/src/main/kotlin/voodoo/cli/VoodooCommand.kt +++ b/voodoo/src/main/kotlin/voodoo/cli/VoodooCommand.kt @@ -34,7 +34,8 @@ class VoodooCommand(invocation: String = "voodoo") : CliktCommand( PackageCommand(), LaunchCommand(), GenerateSchemaCommand(), - UpdateCommand() + UpdateCommand(), + ImportCurseCommand() ) } diff --git a/voodoo/src/main/kotlin/voodoo/config/Configuration.kt b/voodoo/src/main/kotlin/voodoo/config/Configuration.kt index 21a76d9d..5091af93 100644 --- a/voodoo/src/main/kotlin/voodoo/config/Configuration.kt +++ b/voodoo/src/main/kotlin/voodoo/config/Configuration.kt @@ -7,6 +7,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import mu.KotlinLogging import voodoo.pack.EntryOverride +import voodoo.poet.generator.CurseSection import voodoo.util.json import java.io.File @@ -17,11 +18,24 @@ data class Configuration( @JsonSchema.NoDefinition val schema: String = defaultSchema, @Required - val curseforgeGenerators: Map = mapOf(), + val curseforgeGenerators: Map = mapOf( + "Mods" to Generator.Curse( + section = CurseSection.MODS + ), + "ResourcePacks" to Generator.Curse( + section = CurseSection.RESOURCE_PACKS + ) + ), @Required - val forgeGenerators: Map = mapOf(), + val forgeGenerators: Map = mapOf( + "Forge" to Generator.Forge() + ), @Required - val fabricGenerators: Map = mapOf(), + val fabricGenerators: Map = mapOf( + "Fabric" to Generator.Fabric( + requireStable = true + ) + ), @Required val overrides: Map = mapOf() ) { diff --git a/voodoo/src/main/kotlin/voodoo/config/Extensions.kt b/voodoo/src/main/kotlin/voodoo/config/Extensions.kt index 180aae02..f6bdbf4f 100644 --- a/voodoo/src/main/kotlin/voodoo/config/Extensions.kt +++ b/voodoo/src/main/kotlin/voodoo/config/Extensions.kt @@ -10,23 +10,23 @@ import voodoo.pack.VersionPack import voodoo.util.json private val logger = KotlinLogging.logger {} - -fun NestedPack.Companion.generateSchema() = json.encodeToSchema(serializer()) - .replace("\"replace_with_curseforge_projects\"", - Autocompletions.curseforge.keys.joinToString(",") { "\"$it\"" } - ) - .replace("\"replace_with_forge_versions\"", - Autocompletions.forge.keys.joinToString(",") { "\"$it\"" } - ) - .replace("\"replace_with_fabric_intermediaries\"", - Autocompletions.fabricIntermediaries.keys.joinToString(",") { "\"$it\"" } - ) - .replace("\"replace_with_fabric_loaders\"", - Autocompletions.fabricLoaders.keys.joinToString(",") { "\"$it\"" } - ) - .replace("\"replace_with_fabric_installers\"", - Autocompletions.fabricInstallers.keys.joinToString(",") { "\"$it\"" } - ) +// +//fun NestedPack.Companion.generateSchema() = json.encodeToSchema(serializer()) +// .replace("\"replace_with_curseforge_projects\"", +// Autocompletions.curseforge.keys.joinToString(",") { "\"$it\"" } +// ) +// .replace("\"replace_with_forge_versions\"", +// Autocompletions.forge.keys.joinToString(",") { "\"$it\"" } +// ) +// .replace("\"replace_with_fabric_intermediaries\"", +// Autocompletions.fabricIntermediaries.keys.joinToString(",") { "\"$it\"" } +// ) +// .replace("\"replace_with_fabric_loaders\"", +// Autocompletions.fabricLoaders.keys.joinToString(",") { "\"$it\"" } +// ) +// .replace("\"replace_with_fabric_installers\"", +// Autocompletions.fabricInstallers.keys.joinToString(",") { "\"$it\"" } +// ) fun VersionPack.Companion.generateSchema(overridesKeys: Set): String { val fileEntrySchemaObject = buildJsonSchema(FileEntry.serializer(), generateDefinitions = true)