Modrinth: detect default meta-folder, prefer mods to datapacks

Requires specifying datapack-path option to install datapacks (as the location varies between datapack loader mods)
This commit is contained in:
comp500 2023-01-20 04:51:04 +00:00
parent 94a74676c8
commit f3963ec169
2 changed files with 152 additions and 13 deletions

View File

@ -202,8 +202,6 @@ func installVersion(project *modrinthApi.Project, version *modrinthApi.Version,
return errors.New("version doesn't have any files attached")
}
// TODO: explicitly reject modpacks
if len(version.Dependencies) > 0 {
// TODO: could get installed version IDs, and compare to install the newest - i.e. preferring pinned versions over getting absolute latest?
installedProjects := getInstalledProjectIDs(index)
@ -320,7 +318,7 @@ func installVersion(project *modrinthApi.Project, version *modrinthApi.Version,
if cmdshared.PromptYesNo("Would you like to add them? [Y/n]: ") {
for _, v := range depMetadata {
err := createFileMeta(v.projectInfo, v.versionInfo, v.fileInfo, index)
err := createFileMeta(v.projectInfo, v.versionInfo, v.fileInfo, pack, index)
if err != nil {
return err
}
@ -340,9 +338,10 @@ func installVersion(project *modrinthApi.Project, version *modrinthApi.Version,
file = v
}
}
// TODO: handle optional/required resource pack files
// Create the metadata file
err := createFileMeta(project, version, file, index)
err := createFileMeta(project, version, file, pack, index)
if err != nil {
return err
}
@ -364,7 +363,7 @@ func installVersion(project *modrinthApi.Project, version *modrinthApi.Version,
return nil
}
func createFileMeta(project *modrinthApi.Project, version *modrinthApi.Version, file *modrinthApi.File, index *core.Index) error {
func createFileMeta(project *modrinthApi.Project, version *modrinthApi.Version, file *modrinthApi.File, pack core.Pack, index *core.Index) error {
updateMap := make(map[string]map[string]interface{})
var err error
@ -400,8 +399,10 @@ func createFileMeta(project *modrinthApi.Project, version *modrinthApi.Version,
var path string
folder := viper.GetString("meta-folder")
if folder == "" {
folder = "mods"
// TODO: vary based on project type
folder, err = getProjectTypeFolder(*project.ProjectType, version.Loaders, pack.GetLoaders())
if err != nil {
return err
}
}
if project.Slug != nil {
path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, *project.Slug+core.MetaExtension))

View File

@ -3,12 +3,14 @@ package modrinth
import (
modrinthApi "codeberg.org/jmansfield/go-modrinth/modrinth"
"errors"
"fmt"
"github.com/packwiz/packwiz/cmd"
"github.com/packwiz/packwiz/core"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/unascribed/FlexVer/go/flexver"
"golang.org/x/exp/slices"
"math"
"net/http"
"net/url"
"regexp"
@ -61,6 +63,132 @@ var projectTypes = []string{
"mod", "plugin", "datapack", "shader", "resourcepack", "modpack",
}
// "Loaders" that are supported regardless of the configured mod loaders
var defaultMRLoaders = []string{
// TODO: check if Iris/Optifine are installed? suggest installing them?
"iris",
"optifine",
"vanilla", // Core shaders
"minecraft", // Resource packs
}
var withDatapackPathMRLoaders = []string{
"iris",
"optifine",
"vanilla", // Core shaders
"minecraft", // Resource packs
// TODO: check if a datapack loader is installed; suggest installing one?
"datapack", // Datapacks (requires a datapack loader)
}
var loaderFolders = map[string]string{
"quilt": "mods",
"fabric": "mods",
"forge": "mods",
"liteloader": "mods",
"modloader": "mods",
"rift": "mods",
"bukkit": "plugins",
"spigot": "plugins",
"paper": "plugins",
"purpur": "plugins",
"sponge": "plugins",
"bungeecord": "plugins",
"waterfall": "plugins",
"velocity": "plugins",
"iris": "shaderpacks",
"optifine": "shaderpacks",
"vanilla": "resourcepacks",
}
// Preference list for loader types, for comparing files where the version is the same - more preferred is lower
var loaderPreferenceList = []string{
// Prefer quilt versions over fabric versions
"quilt",
"fabric",
"forge",
"liteloader",
"modloader",
"rift",
// Prefer mods to plugins
"sponge",
// Prefer newer Bukkit forks
"purpur",
"paper",
"spigot",
"bukkit",
"velocity",
// Prefer newer BungeeCord forks
"waterfall",
"bungeecord",
// Prefer Iris shaders to Optifine shaders to core shaders
"iris",
"optifine",
"vanilla",
// Prefer mods to datapacks
"datapack",
// Prefer mods to resource packs?! Idk this is just here for completeness
"minecraft",
}
func getMinLoaderIdx(loaders []string) (minIdx int) {
minIdx = math.MaxInt
for _, v := range loaders {
idx := slices.Index(loaderPreferenceList, v)
if idx != -1 && idx < minIdx {
minIdx = idx
}
}
return minIdx
}
func getProjectTypeFolder(projectType string, fileLoaders []string, packLoaders []string) (string, error) {
if projectType == "modpack" {
return "", errors.New("this command should not be used to add Modrinth modpacks, and importing of Modrinth modpacks is not yet supported")
} else if projectType == "resourcepack" {
return "resourcepacks", nil
} else if projectType == "shader" {
bestLoaderIdx := math.MaxInt
for _, v := range fileLoaders {
idx := slices.Index(loaderPreferenceList, v)
if idx != -1 && idx < bestLoaderIdx {
bestLoaderIdx = idx
}
}
if bestLoaderIdx > -1 && bestLoaderIdx < math.MaxInt {
return loaderPreferenceList[bestLoaderIdx], nil
}
return "shaderpacks", nil
} else if projectType == "mod" {
// Look up pack loaders in the list of loaders (note this is currently filtered to quilt/fabric/forge)
bestLoaderIdx := math.MaxInt
for _, v := range fileLoaders {
if slices.Contains(packLoaders, v) {
idx := slices.Index(loaderPreferenceList, v)
if idx != -1 && idx < bestLoaderIdx {
bestLoaderIdx = idx
}
}
}
if bestLoaderIdx > -1 && bestLoaderIdx < math.MaxInt {
return loaderPreferenceList[bestLoaderIdx], nil
}
// Datapack loader is "datapack"
if slices.Contains(fileLoaders, "datapack") {
if viper.GetString("datapack-path") != "" {
return viper.GetString("datapack-path"), nil
} else {
return "", errors.New("set the datapack-path option to use datapacks")
}
}
// Default to "mods" for mod type
return "mods", nil
} else {
return "", fmt.Errorf("unknown project type %s", projectType)
}
}
func parseSlugOrUrl(input string, slug *string, version *string, versionID *string, filename *string) (parsedSlug bool, err error) {
for regexIdx, r := range urlRegexes {
matches := r.FindStringSubmatch(input)
@ -101,27 +229,35 @@ func getLatestVersion(projectID string, pack core.Pack) (*modrinthApi.Version, e
return nil, err
}
gameVersions := append([]string{mcVersion}, viper.GetStringSlice("acceptable-game-versions")...)
var loaders []string
if viper.GetString("datapack-path") != "" {
loaders = append(pack.GetLoaders(), withDatapackPathMRLoaders...)
} else {
loaders = append(pack.GetLoaders(), defaultMRLoaders...)
}
result, err := mrDefaultClient.Versions.ListVersions(projectID, modrinthApi.ListVersionsOptions{
GameVersions: gameVersions,
Loaders: pack.GetLoaders(),
// TODO: change based on project type? or just add iris/optifine/datapack/vanilla/minecraft as default loaders
// TODO: add "datapack" as a loader *if* a path to store datapacks in is configured?
Loaders: loaders,
})
if len(result) == 0 {
return nil, errors.New("no valid versions found")
// TODO: retry with datapack specified, to determine what the issue is? or just request all and filter afterwards
return nil, errors.New("no valid versions found\nUse the acceptable-game-versions option to accept more game versions\nTo use datapacks, add a datapack loader mod and specify the datapack-path option with the location this mod loads datapacks from")
}
latestValidVersion := result[0]
latestValidLoaderIdx := getMinLoaderIdx(result[0].Loaders)
for _, v := range result[1:] {
// Use FlexVer to compare versions
compare := flexver.Compare(*v.VersionNumber, *latestValidVersion.VersionNumber)
if compare == 0 {
// Prefer Quilt over Fabric (Modrinth backend handles filtering)
if slices.Contains(v.Loaders, "quilt") && !slices.Contains(latestValidVersion.Loaders, "quilt") {
loaderIdx := getMinLoaderIdx(v.Loaders)
// Prefer loaders; principally Quilt over Fabric, mods over datapacks (Modrinth backend handles filtering)
if loaderIdx < latestValidLoaderIdx {
latestValidVersion = v
latestValidLoaderIdx = loaderIdx
continue
}
@ -129,9 +265,11 @@ func getLatestVersion(projectID string, pack core.Pack) (*modrinthApi.Version, e
// TODO: flag to force comparing by date?
if v.DatePublished.After(*latestValidVersion.DatePublished) {
latestValidVersion = v
latestValidLoaderIdx = getMinLoaderIdx(v.Loaders)
}
} else if compare > 0 {
latestValidVersion = v
latestValidLoaderIdx = getMinLoaderIdx(v.Loaders)
}
}