jvmMain.de.griefed.serverpackcreator.api.ServerPackHandler.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of serverpackcreator-api-jvm Show documentation
Show all versions of serverpackcreator-api-jvm Show documentation
ServerPackCreators API, to create server packs from Forge, Fabric, Quilt, LegacyFabric and NeoForge modpacks.
/* Copyright (C) 2024 Griefed
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
*/
package de.griefed.serverpackcreator.api
import de.griefed.serverpackcreator.api.modscanning.ModScanner
import de.griefed.serverpackcreator.api.utilities.SimpleStopWatch
import de.griefed.serverpackcreator.api.utilities.common.*
import de.griefed.serverpackcreator.api.versionmeta.VersionMeta
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.ExcludeFileFilter
import net.lingala.zip4j.model.ZipParameters
import java.awt.Image
import java.awt.image.BufferedImage
import java.io.File
import java.io.IOException
import java.net.MalformedURLException
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import java.util.regex.PatternSyntaxException
import javax.imageio.ImageIO
import kotlin.io.path.absolute
/**
* Everything revolving around creating a server pack. The intended workflow is to create a [PackConfig] and run
* it through any of the available [ConfigurationHandler.checkConfiguration]-variants, and then call [run] with the
* previously checked configuration model. You may run with an unchecked configuration model, but no guarantees or
* promises, yes not even support, is given for running a model without checking it first.
*
* This class also gives you access to the methods which are responsible for creating the server pack, in case you want
* to do things manually.
*
* The methods in question are:
* * [cleanupEnvironment]
* * [ApiPlugins.runPreZipExtensions]
* * [copyFiles]
* * [getImprovedFabricLauncher] if Fabric is the chosen Modloader
* * [copyIcon]
* * [copyProperties]
* * [ApiPlugins.runPreZipExtensions]
* * [zipBuilder]
* * [createStartScripts]
* * [ApiPlugins.runPostGenExtensions]
*
* If you want to execute extensions, see
* * [ApiPlugins.runPreGenExtensions]},
* * [ApiPlugins.runPreZipExtensions]} and
* * [ApiPlugins.runPostGenExtensions].
*
* @param apiProperties Base settings of ServerPackCreator needed for server pack generation, such as access to the
* directories, script templates and so on.
* @param versionMeta Meta for modloader and version specific checks and information gathering, such as modloader
* installer downloads.
* @param utilities Common utilities used across ServerPackCreator.
* @param apiPlugins Any addons which a user may want to execute during the generation of a server pack.
* @param modScanner In case a user enabled automatic sideness detection, this will exclude clientside-only mods
* from a server pack.
*
* @author Griefed
*/
actual class ServerPackHandler actual constructor(
private val apiProperties: ApiProperties,
private val versionMeta: VersionMeta,
private val utilities: Utilities,
private val apiPlugins: ApiPlugins,
private val modScanner: ModScanner
) : ServerPack, TreeSet>() {
override fun getServerPackDestination(packConfig: Pack<*, *, *>): String {
var serverPackToBe = File(packConfig.modpackDir).name + packConfig.serverPackSuffix
serverPackToBe = utilities.stringUtilities.pathSecureText(serverPackToBe.replace(" ", "_"))
return File(apiProperties.serverPacksDirectory, serverPackToBe).path
}
override fun run(packConfig: PackConfig): Boolean {
val destination = getServerPackDestination(packConfig)
/*
* Check whether the server pack for the specified modpack already exists and whether overwrite is disabled.
* If the server pack exists and overwrite is disabled, no new server pack will be generated.
*/
if (!apiProperties.isServerPacksOverwriteEnabled && File(destination).exists()) {
log.info("Server pack already exists and overwrite disabled.")
} else {
// Make sure no files from previously generated server packs interrupt us.
cleanupEnvironment(true, destination)
val generationStopWatch = SimpleStopWatch().start()
try {
File(destination).createDirectories(create = true, directory = true)
} catch (ignored: IOException) {
}
apiPlugins.runPreGenExtensions(packConfig, destination)
// Recursively copy all specified directories and files, excluding clientside-only mods, to server pack.
copyFiles(packConfig)
// If true, copy the server-icon.png from server_files to the server pack.
if (packConfig.isServerIconInclusionDesired) {
copyIcon(packConfig)
} else {
log.info("Not including servericon.")
}
// If true, copy the server.properties from server_files to the server pack.
if (packConfig.isServerPropertiesInclusionDesired) {
copyProperties(packConfig)
} else {
log.info("Not including server.properties.")
}
apiPlugins.runPreZipExtensions(packConfig, destination)
// If true, create a ZIP-archive excluding the Minecraft server JAR of the server pack.
if (packConfig.isZipCreationDesired) {
/*
* Create the start scripts for this server pack. Ignores custom SPC_JAVA_SPC setting if one
* is present. This is because a ZIP-archive, if one is created, is supposed to be uploaded
* to platforms like CurseForge. We must not have scripts with custom Java paths there.
*/
createStartScripts(packConfig, false)
zipBuilder(packConfig)
} else {
log.info("Not creating zip archive of serverpack.")
}
/*
* If modloader is fabric, try and replace the old server-launch.jar with the new and improved
* one which also downloads the Minecraft server.
*/
if (packConfig.modloader.equals("Fabric", ignoreCase = true)) {
provideImprovedFabricServerLauncher(packConfig)
}
/*
* Create the start scripts for this server pack to be used for local testing.
* The difference to the previous call is that these scripts respect the SPC_JAVA_SPC
* placeholder setting, if the user has set one
*/
createStartScripts(packConfig, true)
// Inform user about location of newly generated server pack.
log.info("Server pack available at: $destination")
log.info("Server pack archive available at: ${destination}_server_pack.zip")
log.info("Done!")
apiPlugins.runPostGenExtensions(packConfig, destination)
log.debug("Generation took ${generationStopWatch.stop().getTime()}")
}
return true
}
override fun cleanupEnvironment(deleteZip: Boolean, destination: String) {
log.info("Found old server pack at $destination. Cleaning up...")
File(destination).deleteQuietly()
if (deleteZip) {
File(destination + "_server_pack.zip").deleteQuietly()
}
}
override fun copyFiles(
modpackDir: String,
inclusions: ArrayList,
clientMods: List,
whitelist: List,
minecraftVersion: String,
destination: String,
modloader: String
) {
val exclusions = mutableListOf()
var acquired: List
val serverPackFiles: MutableList = ArrayList(100000)
try {
File(destination).createDirectories()
} catch (ex: IOException) {
log.error("Failed to create directory $destination")
}
if (inclusions.size == 1 && inclusions[0].source == "lazy_mode") {
log.warn("!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!")
log.warn("Lazy mode specified. This will copy the WHOLE modpack to the server pack. No exceptions.")
log.warn("You will not receive any support for a server pack generated this way.")
log.warn("Do not open an issue on GitHub if this configuration errors or results in a broken server pack.")
log.warn("!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!WARNING!!!")
try {
File(modpackDir).copyRecursively(File(destination), true)
} catch (ex: IOException) {
log.error("An error occurred copying the modpack to the server pack in lazy mode.", ex)
}
return
}
for (inclusion in inclusions) {
acquired = getServerFiles(
inclusion,
modpackDir,
destination,
exclusions,
clientMods,
whitelist,
minecraftVersion,
modloader
)
serverPackFiles.addAll(acquired)
}
log.info("Ensuring files and/or directories are properly excluded.")
serverPackFiles.removeIf { it: ServerPackFile ->
excludeFileOrDirectory(modpackDir, it.sourceFile, exclusions)
}
log.info("Copying files to the server pack. This may take a while...")
for (file in serverPackFiles) {
try {
file.copy()
} catch (ex: IOException) {
log.error(
"An error occurred trying to copy " + file.sourceFile + " to " + file.destinationFile + ".",
ex
)
}
}
}
fun getServerFiles(
inclusion: InclusionSpecification,
modpackDir: String,
destination: String,
exclusions: MutableList,
clientMods: List,
whitelist: List,
minecraftVersion: String,
modloader: String
): List {
val serverPackFiles = mutableListOf()
val clientDir = File(modpackDir, inclusion.source)
val serverDir = File(destination, inclusion.source)
val acquired: List
val processed: List
val serverPackFile: ServerPackFile
val inclusionSourceFile = File(inclusion.source).absoluteFile
val inclusionDestinationFile = File(destination, inclusionSourceFile.name).absoluteFile
when {
inclusion.isGlobalFilter() -> {
if (inclusion.hasExclusionFilter()) {
try {
exclusions.add(inclusion.exclusionFilter!!.toRegex())
} catch (ex: PatternSyntaxException) {
log.error("Invalid exclusion-regex specified: ${inclusion.exclusionFilter}.",ex)
}
}
}
inclusion.hasDestination() -> {
val destinationFile = File(destination,inclusion.destination ?: inclusionSourceFile.name)
when {
clientDir.isDirectory -> {
acquired = getExplicitFiles(clientDir.absolutePath, inclusion.destination!!, modpackDir, destination)
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
clientDir.absoluteFile.isFile -> {
serverPackFile = ServerPackFile(clientDir, destinationFile)
serverPackFiles.add(serverPackFile)
}
inclusionSourceFile.isDirectory -> {
acquired = getExplicitFiles(inclusion.source, inclusion.destination!!, modpackDir, destination)
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
inclusionSourceFile.isFile -> {
serverPackFile = ServerPackFile(inclusionSourceFile, destinationFile)
serverPackFiles.add(serverPackFile)
}
else -> {
serverPackFile = ServerPackFile(inclusionSourceFile, destinationFile)
serverPackFiles.add(serverPackFile)
}
}
}
inclusion.source == "mods" -> {
try {
serverDir.createDirectories()
} catch (ignored: IOException) {
}
acquired = mutableListOf()
for (mod in getModsToInclude(clientDir.absolutePath, clientMods, whitelist, minecraftVersion, modloader)) {
acquired.add(ServerPackFile(mod, File(serverDir, mod.name)))
}
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
clientDir.absoluteFile.isDirectory -> {
acquired = getDirectoryFiles(clientDir.absolutePath, serverDir.absolutePath)
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
clientDir.absoluteFile.isFile -> {
serverPackFile = ServerPackFile(clientDir, serverDir)
serverPackFiles.add(serverPackFile)
}
inclusionSourceFile.isFile -> {
serverPackFile = ServerPackFile(inclusionSourceFile, inclusionDestinationFile)
serverPackFiles.add(serverPackFile)
}
inclusionSourceFile.isDirectory -> {
acquired = getDirectoryFiles(inclusionSourceFile.absolutePath, inclusionDestinationFile.absolutePath)
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
else -> {
acquired = getDirectoryFiles(clientDir.absolutePath, serverDir.absolutePath)
processed = runFilters(acquired, inclusion, modpackDir)
serverPackFiles.addAll(processed)
}
}
return serverPackFiles
}
private fun runFilters(
acquired: List,
inclusionSpec: InclusionSpecification,
modpackDir: String
): List {
val processed = mutableListOf()
val inclusionFilter = if (inclusionSpec.inclusionFilter.isNullOrBlank()) {
null
} else {
try {
inclusionSpec.inclusionFilter!!.toRegex()
} catch (ex: PatternSyntaxException) {
log.error("Invalid inclusion-regex specified: ${inclusionSpec.inclusionFilter}.",ex)
null
}
}
val exclusionFilter = if (inclusionSpec.exclusionFilter.isNullOrBlank()) {
null
} else {
try {
inclusionSpec.exclusionFilter!!.toRegex()
} catch (ex: PatternSyntaxException) {
log.error("Invalid exclusion-regex specified: ${inclusionSpec.exclusionFilter}.",ex)
null
}
}
if (inclusionFilter != null) {
for (file in acquired) {
if (file.sourceFile.absolutePath.replace(modpackDir + File.separator, "").matches(inclusionFilter)) {
processed.add(file)
log.debug("{} matched Inclusion-Filter {}.", file, inclusionFilter)
}
}
} else {
processed.addAll(acquired)
}
if (exclusionFilter != null) {
processed.removeIf { file ->
val source = file.sourceFile.absolutePath.replace(modpackDir + File.separator, "")
return@removeIf if (source.matches(exclusionFilter)) {
log.debug("{} matched Inclusion-Filter {}.", file.sourceFile, exclusionFilter)
true
} else {
false
}
}
}
return processed
}
override fun getImprovedFabricLauncher(minecraftVersion: String, fabricVersion: String, destination: String) {
val fileDestination = File(destination, "fabric-server-launcher.jar")
if (versionMeta.fabric.launcherFor(minecraftVersion, fabricVersion).isPresent) {
versionMeta.fabric.launcherFor(minecraftVersion, fabricVersion).get().copyTo(fileDestination)
log.info("Successfully provided improved Fabric Server Launcher.")
val text = """
|If you are using this server pack on a managed server, meaning you can not execute scripts, please use the fabric-server-launcher.jar instead of the fabric-server-launch.jar. Note the extra "er" at the end of "launcher".
|This is the improved Fabric Server Launcher, which will take care of downloading and installing the Minecraft server and any and all libraries needed for running the Fabric server.
|
|The downside of this method is the occasional incompatibility of mods with the Fabric version, as the new Fabric Server Launcher always uses the latest available Fabric version.
|If a mod is incompatible with said latest Fabric version, contact the mod-author and ask them to remedy the situation.
|The official Fabric Discord had the following to add to this:
| Fabric loader however is cross version, so unless there is a mod incompatibility (which usually involves the mod being broken / using non-api internals)
| there is no good reason to use anything but the latest. I.e. the latest loader on any Minecraft version works with the new server launcher.
""".trimMargin()
File(destination, "SERVER_PACK_INFO.txt").writeText(text)
}
}
override fun copyIcon(destination: String, pathToServerIcon: String) {
log.info("Copying server-icon.png...")
val customIcon = File(destination, apiProperties.defaultServerIcon.name)
if (File(pathToServerIcon).exists()) {
try {
val originalImage: BufferedImage = ImageIO.read(File(pathToServerIcon))
if (originalImage.height == 64 && originalImage.width == 64) {
try {
File(pathToServerIcon).copyTo(customIcon, true)
} catch (e: IOException) {
log.error("An error occurred trying to copy the server-icon.", e)
}
} else {
val scaledImage: Image = originalImage.getScaledInstance(64, 64, Image.SCALE_SMOOTH)
val outputImage = BufferedImage(
scaledImage.getWidth(null), scaledImage.getHeight(null), BufferedImage.TYPE_INT_ARGB
)
outputImage.graphics.drawImage(scaledImage, 0, 0, null)
try {
ImageIO.write(outputImage, "png", customIcon)
} catch (ex: IOException) {
log.error("Error scaling image.", ex)
}
}
} catch (ex: Exception) {
log.error("Error reading server-icon image.", ex)
}
} else if (pathToServerIcon.isEmpty()) {
log.info("No custom icon specified or the file doesn't exist.")
apiProperties.defaultServerIcon.copyTo(customIcon, true)
} else {
log.error("The specified server-icon does not exist: $pathToServerIcon")
}
}
override fun copyProperties(destination: String, pathToServerProperties: String) {
log.info("Copying server.properties...")
val customProperties = File(destination, apiProperties.defaultServerProperties.name)
if (File(pathToServerProperties).exists()) {
File(pathToServerProperties).copyTo(customProperties, true)
} else if (pathToServerProperties.isEmpty()) {
log.info("No custom properties specified or the file doesn't exist.")
apiProperties.defaultServerProperties.copyTo(customProperties, true)
} else {
log.error("The specified server.properties does not exist: $pathToServerProperties")
}
}
override fun createStartScripts(scriptSettings: HashMap, destination: String, isLocal: Boolean) {
for (template in apiProperties.scriptTemplates) {
try {
val fileEnding = template.toString().substring(template.toString().lastIndexOf(".") + 1)
val destinationScript = File(destination, "start.$fileEnding")
var scriptContent: String = template.readText()
scriptContent = replacePlaceholders(isLocal, scriptContent, scriptSettings)
destinationScript.writeText(scriptContent.replace("\r", ""))
} catch (ex: Exception) {
log.error("File not accessible: $template.", ex)
}
}
try {
val destinationVariables = File(destination, "variables.txt")
var variablesContent = variables
variablesContent = replacePlaceholders(isLocal, variablesContent, scriptSettings)
destinationVariables.writeText(variablesContent.replace("\r", ""))
} catch (ex: Exception) {
log.error("File not accessible: ${File(destination, "variables.txt")}.", ex)
}
}
override fun zipBuilder(
minecraftVersion: String,
destination: String,
modloader: String,
modloaderVersion: String
) {
log.info("Creating zip archive of serverpack...")
val zipParameters = ZipParameters()
val filesToExclude: MutableList = ArrayList(100)
if (apiProperties.isZipFileExclusionEnabled) {
for (entry in apiProperties.zipArchiveExclusions) {
filesToExclude.add(
File(
destination,
entry.replace("MINECRAFT_VERSION", minecraftVersion).replace("MODLOADER", modloader)
.replace("MODLOADER_VERSION", modloaderVersion)
)
)
}
val excludeFileFilter = ExcludeFileFilter { o: File -> filesToExclude.contains(o) }
zipParameters.excludeFileFilter = excludeFileFilter
} else {
log.info("File exclusion from ZIP-archives deactivated.")
}
val comment = ("Server pack made with ServerPackCreator ${apiProperties.apiVersion} by Griefed.")
zipParameters.isIncludeRootFolder = false
zipParameters.fileComment = comment
try {
ZipFile("${destination}_server_pack.zip").use {
it.addFolder(File(destination), zipParameters)
it.comment = comment
}
} catch (ex: IOException) {
log.error("There was an error during zip creation.", ex)
}
log.info("Finished creation of zip archive.")
}
override fun preInstallationCleanup(destination: String) {
log.info("Pre server installation cleanup.")
var fileToDelete: File
for (file in apiProperties.preInstallCleanupFiles) {
fileToDelete = File(destination,file)
if (fileToDelete.deleteQuietly()) {
log.info("Deleted $fileToDelete")
}
}
}
override fun getExplicitFiles(
source: String,
destination: String,
modpackDir: String,
serverPackDestination: String
): MutableList {
val serverPackFiles: MutableList = ArrayList(100)
if (File(modpackDir, source).isFile) {
serverPackFiles.add(
ServerPackFile(
File(modpackDir, source), File(serverPackDestination, destination)
)
)
} else if (File(modpackDir, source).isDirectory) {
serverPackFiles.addAll(
getDirectoryFiles(
modpackDir + File.separator + source, serverPackDestination + File.separator + destination
)
)
} else if (File(source).isFile) {
serverPackFiles.add(
ServerPackFile(
File(source), File(serverPackDestination, destination)
)
)
} else if (File(source).isDirectory) {
serverPackFiles.addAll(
getDirectoryFiles(
source, serverPackDestination + File.separator + destination
)
)
}
return serverPackFiles
}
override fun getSaveFiles(clientDir: String, directory: String, destination: String): List {
val serverPackFiles: MutableList = ArrayList(2000)
try {
Files.walk(Paths.get(clientDir)).use {
for (path in it) {
try {
serverPackFiles.add(
ServerPackFile(
path,
Paths.get(destination + File.separator + directory.substring(6))
.resolve(Paths.get(clientDir).relativize(path))
)
)
} catch (ex: UnsupportedOperationException) {
log.error("Couldn't gather file $path from directory $clientDir.", ex)
}
}
}
} catch (ex: IOException) {
log.error("An error occurred during the copy-procedure to the server pack.", ex)
}
return serverPackFiles
}
override fun getModsToInclude(
modsDir: String,
userSpecifiedClientMods: List,
userSpecifiedModsWhitelist: List,
minecraftVersion: String,
modloader: String
): List {
log.info("Preparing a list of mods to include in server pack...")
val filesInModsDir: Collection = File(modsDir).filteredWalk(modFileEndings, FilterType.ENDS_WITH, FileWalkDirection.TOP_DOWN, recursive = false)
val modsInModpack = TreeSet(filesInModsDir)
val autodiscoveredClientMods: MutableList = ArrayList(100)
// Check whether scanning mods for sideness is activated.
if (apiProperties.isAutoExcludingModsEnabled) {
val scanningStopWatch = SimpleStopWatch().start()
when (modloader) {
"LegacyFabric", "Fabric" -> autodiscoveredClientMods.addAll(modScanner.fabricScanner.scan(filesInModsDir))
"Forge" -> if (minecraftVersion.split(".").dropLastWhile { it.isEmpty() }
.toTypedArray()[1].toInt() > 12) {
autodiscoveredClientMods.addAll(modScanner.tomlScanner.scan(filesInModsDir))
} else {
autodiscoveredClientMods.addAll(modScanner.annotationScanner.scan(filesInModsDir))
}
"Quilt" -> {
val discoMods = TreeSet()
discoMods.addAll(modScanner.fabricScanner.scan(filesInModsDir))
discoMods.addAll(modScanner.quiltScanner.scan(filesInModsDir))
autodiscoveredClientMods.addAll(discoMods)
discoMods.clear()
}
}
// Exclude scanned mods from copying if said functionality is enabled.
excludeMods(autodiscoveredClientMods, modsInModpack)
log.debug(
"Scanning and excluding of ${filesInModsDir.size} mods took ${scanningStopWatch.stop().getTime()}"
)
} else {
log.info("Automatic clientside-only mod detection disabled.")
}
// Exclude user-specified mods from copying.
excludeUserSpecifiedMod(userSpecifiedClientMods, userSpecifiedModsWhitelist, modsInModpack)
return ArrayList(modsInModpack)
}
override fun getDirectoryFiles(source: String, destination: String): List {
val serverPackFiles: MutableList = ArrayList(100)
try {
Files.walk(Paths.get(source).absolute()).use {
for (path in it) {
try {
val pathFile = path.toFile().absolutePath
val sourceFile = File(source).absolutePath
val destFile = File(destination, pathFile.replace(sourceFile, ""))
serverPackFiles.add(
ServerPackFile(
path.toFile(),
destFile
)
)
} catch (ex: UnsupportedOperationException) {
log.error("Couldn't gather file $path from directory $source.", ex)
}
}
}
} catch (ex: IOException) {
log.error("An error occurred gathering files to copy to the server pack for directory $source.", ex)
}
return serverPackFiles
}
override fun excludeFileOrDirectory(modpackDir: String, fileToCheckFor: File, exclusions: List): Boolean {
val cleaned = fileToCheckFor.absolutePath.replace(File(modpackDir).absolutePath + File.separator, "")
return exclusions.any { regex ->
if (cleaned.matches(regex)) {
log.info("Excluding '$cleaned' as per global exclusion filter '$regex'.")
return@any true
} else {
return@any false
}
}
}
override fun serverDownloadable(mcVersion: String, modloader: String, modloaderVersion: String) = when (modloader) {
"Fabric" -> utilities.webUtilities.isReachable(versionMeta.fabric.releaseInstallerUrl())
"Forge" -> {
val instance = versionMeta.forge.getForgeInstance(mcVersion, modloaderVersion)
instance.isPresent && utilities.webUtilities.isReachable(instance.get().installerUrl)
}
"Quilt" -> utilities.webUtilities.isReachable(versionMeta.quilt.releaseInstallerUrl())
"LegacyFabric" -> {
try {
utilities.webUtilities.isReachable(versionMeta.legacyFabric.releaseInstallerUrl())
} catch (e: MalformedURLException) {
false
}
}
"NeoForge" -> {
val instance = versionMeta.neoForge.getNeoForgeInstance(mcVersion,modloaderVersion)
instance.isPresent && utilities.webUtilities.isReachable(instance.get().installerUrl)
}
else -> false
}
override fun postInstallCleanup(destination: String) {
log.info("Cleanup after modloader server installation.")
var fileToDelete: File
for (file in apiProperties.postInstallCleanupFiles) {
fileToDelete = File(destination, file)
if (fileToDelete.deleteQuietly()) {
log.info(" Deleted $fileToDelete")
}
}
}
override fun excludeMods(autodiscoveredClientMods: List, modsInModpack: TreeSet) {
if (autodiscoveredClientMods.isNotEmpty()) {
log.info("Automatically detected mods: ${autodiscoveredClientMods.size}")
for (discoveredMod in autodiscoveredClientMods) {
modsInModpack.removeIf {
if (it.name.contains(discoveredMod.name)) {
log.warn("Automatically excluding mod: ${discoveredMod.name}")
return@removeIf true
} else {
return@removeIf false
}
}
}
} else {
log.info("No clientside-only mods detected.")
}
}
override fun excludeUserSpecifiedMod(userSpecifiedExclusions: List, userSpecifiedModsWhitelist: List, modsInModpack: TreeSet) {
if (userSpecifiedExclusions.isNotEmpty()) {
log.info("Performing ${apiProperties.exclusionFilter}-type checks for user-specified clientside-only mod exclusion.")
for (userSpecifiedExclusion in userSpecifiedExclusions) {
exclude(userSpecifiedExclusion, userSpecifiedModsWhitelist, modsInModpack)
}
} else {
log.warn("User specified no clientside-only mods.")
}
}
override fun regexWalk(
source: File, destination: String, regex: Regex, serverPackFiles: MutableList
) {
var toMatch: String
try {
Files.walk(source.toPath()).use {
for (path in it) {
toMatch = path.toFile().absolutePath.replace(source.absolutePath, "")
if (toMatch.startsWith(File.separator)) {
toMatch = toMatch.substring(1)
}
if (toMatch.matches(regex)) {
val add = Paths.get(destination + File.separator + source.name)
.resolve(source.toPath().relativize(path))
serverPackFiles.add(
ServerPackFile(
path, add
)
)
log.debug("Including through regex-match:")
log.debug(" SOURCE: {}", path)
log.debug(" DESTINATION: {}", add)
}
}
}
} catch (ex: IOException) {
log.error("Couldn't gather all files from ${source.absolutePath} for filter \"$regex\".", ex)
}
}
override fun exclude(userSpecifiedExclusion: String, userSpecifiedModsWhitelist: List, modsInModpack: TreeSet) {
modsInModpack.removeIf { modToCheck ->
val excluded: Boolean
val modName = modToCheck.name
excluded = when (apiProperties.exclusionFilter) {
ExclusionFilter.START -> modName.startsWith(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.startsWith(whitelistedMod) }
ExclusionFilter.END -> modName.endsWith(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.endsWith(whitelistedMod) }
ExclusionFilter.CONTAIN -> modName.contains(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.contains(whitelistedMod) }
ExclusionFilter.REGEX -> modName.matches(userSpecifiedExclusion.toRegex()) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.matches(whitelistedMod.toRegex()) }
ExclusionFilter.EITHER -> (
(modName.startsWith(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.startsWith(whitelistedMod) }) ||
(modName.endsWith(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.endsWith(whitelistedMod) }) ||
(modName.contains(userSpecifiedExclusion) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.contains(whitelistedMod) }) ||
(modName.matches(userSpecifiedExclusion.toRegex()) && !userSpecifiedModsWhitelist.any { whitelistedMod -> modName.matches(whitelistedMod.toRegex()) }))
}
if (excluded) {
log.debug("Removed ${modToCheck.name} as per user-specified check: $userSpecifiedExclusion")
}
excluded
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy