@BloodWorkXGaming Kannst du das Vlt. fixen???
I went on for 3 Hours making an older version of this compatible with the new API
and now i am here to see it has already been done???
aww man i need to sleep now
i literally sat there for 4 hours implementing the new api because i thought i had to do it myself...
anyways i did this:
--- Edit -> I downloaded the serverstarter from here to find out my pain was not in vain
The following is a Problem:
the page you used in your project as an api got removed
I edited the code so that this Error:
[12:31:02] [ERROR] Error while trying to get URL from cursemeta for mod ModEntryRaw(projectID=405744, fileID=3296428)
java.io.IOException: Request to https://cursemeta.dries007.net/405744/3296428.json was not successful.
at atm.bloodworkxgaming.serverstarter.packtype.curse.CursePackType.downloadMods$lambda-3(CursePackType.kt:203)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:754)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Does not happen anymore
you may use this code in your project
you are welcome
(maaan i really felt so dumb when i read the latest push: moved to new api, But it turns out i wasn't wrong...)
package atm.bloodworkxgaming.serverstarter.packtype.curse
import atm.bloodworkxgaming.serverstarter.InternetManager
import atm.bloodworkxgaming.serverstarter.ServerStarter.Companion.LOGGER
import atm.bloodworkxgaming.serverstarter.config.ConfigFile
import atm.bloodworkxgaming.serverstarter.packtype.AbstractZipbasedPackType
import atm.bloodworkxgaming.serverstarter.packtype.writeToFile
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import okhttp3.Request
import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStreamReader
import java.net.URISyntaxException
import java.nio.file.PathMatcher
import java.nio.file.Paths
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicInteger
import java.util.regex.Pattern
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
open class CursePackType(private val configFile: ConfigFile) : AbstractZipbasedPackType(configFile) {
private var forgeVersion: String = configFile.install.loaderVersion
private var mcVersion: String = configFile.install.mcVersion
private val oldFiles = File(basePath + "OLD_TO_DELETE/")
override fun cleanUrl(url: String): String {
if (url.contains("curseforge.com") && !url.endsWith("/download"))
return "$url/download"
return url
}
/**
* Gets the forge version, can be based on the version from the downloaded pack
*
* @return String representation of the version
*/
override fun getForgeVersion(): String {
return forgeVersion
}
/**
* Gets the forge version, can be based on the version from the downloaded pack
*
* @return String representation of the version
*/
override fun getMCVersion(): String {
return mcVersion
}
@Throws(IOException::class)
override fun handleZip(file: File, pathMatchers: List<PathMatcher>) {
// delete old installer folder
FileUtils.deleteDirectory(oldFiles)
// start with deleting the mods folder as it is not guaranteed to have override mods
val modsFolder = File(basePath + "mods/")
if (modsFolder.exists())
FileUtils.moveDirectory(modsFolder, File(oldFiles, "mods"))
LOGGER.info("Moved the mods folder")
LOGGER.info("Starting to unzip files.")
// unzip start
try {
ZipInputStream(FileInputStream(file)).use { zis ->
var entry: ZipEntry? = zis.nextEntry
loop@ while (entry != null) {
LOGGER.info("Entry in zip: $entry", true)
val name = entry.name
// special manifest treatment
if (name == "manifest.json")
zis.writeToFile(File(basePath + "manifest.json"))
// overrides
if (name.startsWith("overrides/")) {
val path = entry.name.substring(10)
when {
pathMatchers.any { it.matches(Paths.get(path)) } ->
LOGGER.info("Skipping $path as it is on the ignore List.", true)
!name.endsWith("/") -> {
val outfile = File(basePath + path)
LOGGER.info("Copying zip entry to = $outfile", true)
outfile.parentFile?.mkdirs()
zis.writeToFile(outfile)
}
name != "overrides/" -> {
val newFolder = File(basePath + path)
if (newFolder.exists())
FileUtils.moveDirectory(newFolder, File(oldFiles, path))
LOGGER.info("Folder moved: " + newFolder.absolutePath, true)
}
}
}
entry = zis.nextEntry
}
zis.closeEntry()
}
} catch (e: IOException) {
LOGGER.error("Could not unzip files", e)
}
LOGGER.info("Done unzipping the files.")
}
@Throws(IOException::class)
override fun postProcessing() {
val mods = ArrayList<ModEntryRaw>()
InputStreamReader(FileInputStream(File(basePath + "manifest.json")), "utf-8").use { reader ->
val json = JsonParser().parse(reader).asJsonObject
LOGGER.info("manifest JSON Object: $json", true)
val mcObj = json.getAsJsonObject("minecraft")
if (mcVersion.isEmpty()) {
mcVersion = mcObj.getAsJsonPrimitive("version").asString
}
// gets the forge version
if (forgeVersion.isEmpty()) {
val loaders = mcObj.getAsJsonArray("modLoaders")
if (loaders.size() > 0) {
forgeVersion = loaders[0].asJsonObject.getAsJsonPrimitive("id").asString.substring(6)
}
}
// gets all the mods
for (jsonElement in json.getAsJsonArray("files")) {
val obj = jsonElement.asJsonObject
mods.add(ModEntryRaw(
obj.getAsJsonPrimitive("projectID").asString,
obj.getAsJsonPrimitive("fileID").asString))
}
}
downloadMods(mods)
}
/**
* Downloads the mods specified in the manifest
* Gets the data from cursemeta
*
* @param mods List of the mods from the manifest
*/
private fun downloadMods(mods: List<ModEntryRaw>) {
val ignoreSet = HashSet<String>()
val ignoreListTemp = configFile.install.getFormatSpecificSettingOrDefault<List<Any>>("ignoreProject", null)
if (ignoreListTemp != null)
for (o in ignoreListTemp) {
if (o is String)
ignoreSet.add(o)
if (o is Int)
ignoreSet.add(o.toString())
}
val urls = ConcurrentLinkedQueue<String>()
LOGGER.info("Requesting Download links from cursemeta.")
mods.parallelStream().forEach { mod ->
if (ignoreSet.isNotEmpty() && ignoreSet.contains(mod.projectID)) {
LOGGER.info("Skipping mod with projectID: " + mod.projectID)
return@forEach
}
val url = "https://api.cfwidget.com/${mod.projectID}?version=${mod.fileID}"
LOGGER.info("Download url is: $url", true)
try {
val request = Request.Builder()
.url(url)
.header("User-Agent", "All the mods server installer.")
.header("Content-Type", "application/json")
.build()
val res = InternetManager.httpClient.newCall(request).execute()
if (!res.isSuccessful)
throw IOException("Request to $url was not successful.")
val body = res.body ?: throw IOException("Request to $url returned a null body.")
try {
val jsonRes = JsonParser().parse(body.string()).asJsonObject
LOGGER.info("Response from manifest query: $jsonRes", true)
var arr = jsonRes.asJsonObject.getAsJsonObject("download");
var part1 = mod.fileID.subSequence(0, 4).toString();
var part2 = mod.fileID.split(part1)[1];
// https://mediafilez.forgecdn.net/files/2743/38/useful_backpacks-1.16.5-1.12.1.90.jar
// https://mediafilez.forgecdn.net/files/3350/860/useful_backpacks-1.16.5-1.12.1.90.jar
// https://mediafilez.forgecdn.net/files/3871/353/MouseTweaks-forge-mc1.19-2.23.jar
// https://mediafilez.forgecdn.net/files/3398//CosmeticArmorReworked-1.16.5-v4.jar
// https://mediafilez.forgecdn.net/files/3398/0/CosmeticArmorReworked-1.16.5-v4.jar
// https://mediafilez.forgecdn.net/files/3407/020/archers_paradox-1.16.5-1.3.1.jar
// https://mediafilez.forgecdn.net/files/3407/20/archers_paradox-1.16.5-1.3.1.jar
val regx = "^0+\$".toRegex();
if(!regx.containsMatchIn(part2))
part2 = part2.replace("^0*".toRegex(),"");
else
part2 = "0";
var url = "https://mediafilez.forgecdn.net/files/$part1/$part2/${arr["name"].asString.replace("+","%2B")}"
LOGGER.info(url)
urls.add(url)
}
catch(e:Exception)
{
}
} catch (e: IOException) {
LOGGER.error("Error while trying to get URL from cursemeta for mod $mod", e)
}
}
LOGGER.info("Mods to download: $urls", true)
processMods(urls)
}
/**
* Downloads all mods, with a second fallback if failed
* This is done in parallel for better performance
*
* @param mods List of urls
*/
private fun processMods(mods: Collection<String>) {
// constructs the ignore list
val ignorePatterns = ArrayList<Pattern>()
for (ignoreFile in configFile.install.ignoreFiles) {
if (ignoreFile.startsWith("mods/")) {
ignorePatterns.add(Pattern.compile(ignoreFile.substring(ignoreFile.lastIndexOf('/'))))
}
}
// downloads the mods
val count = AtomicInteger(0)
val totalCount = mods.size
val fallbackList = ArrayList<String>()
mods.stream().parallel().forEach { s -> processSingleMod(s, count, totalCount, fallbackList, ignorePatterns) }
val secondFail = ArrayList<String>()
fallbackList.forEach { s -> processSingleMod(s, count, totalCount, secondFail, ignorePatterns) }
if (secondFail.isNotEmpty()) {
LOGGER.warn("Failed to download (a) mod(s):")
for (s in secondFail) {
LOGGER.warn("\t" + s)
}
}
}
/**
* Downloads a single mod and saves to the /mods directory
*
* @param mod URL of the mod
* @param counter current counter of how many mods have already been downloaded
* @param totalCount total count of mods that have to be downloaded
* @param fallbackList List to write to when it failed
* @param ignorePatterns Patterns of mods which should be ignored
*/
private fun processSingleMod(mod: String, counter: AtomicInteger, totalCount: Int, fallbackList: MutableList<String>, ignorePatterns: List<Pattern>) {
try {
val modName = FilenameUtils.getName(mod)
for (ignorePattern in ignorePatterns) {
if (ignorePattern.matcher(modName).matches()) {
LOGGER.info("[" + counter.incrementAndGet() + "/" + totalCount + "] Skipped ignored mod: " + modName)
}
}
InternetManager.downloadToFile(mod, File(basePath + "mods/" + modName))
LOGGER.info("[" + String.format("% 3d", counter.incrementAndGet()) + "/" + totalCount + "] Downloaded mod: " + modName)
} catch (e: IOException) {
LOGGER.error("Failed to download mod", e)
fallbackList.add(mod)
} catch (e: URISyntaxException) {
LOGGER.error("Invalid url for $mod", e)
}
}
}
/**
* Data class to keep projectID and fileID together
*/
data class ModEntryRaw(val projectID: String, val fileID: String)
@BloodWorkXGaming Kannst du das Vlt. fixen??? I went on for 3 Hours making an older version of this compatible with the new API and now i am here to see it has already been done??? aww man i need to sleep now i literally sat there for 4 hours implementing the new api because i thought i had to do it myself... anyways i did this:
--- Edit -> I downloaded the serverstarter from here to find out my pain was not in vain The following is a Problem: the page you used in your project as an api got removed I edited the code so that this Error:
Does not happen anymore you may use this code in your project you are welcome
(maaan i really felt so dumb when i read the latest push: moved to new api, But it turns out i wasn't wrong...)