mirror of
https://github.com/packwiz/packwiz.git
synced 2025-04-19 21:16:30 +02:00
Add init command
This commit is contained in:
parent
4fea7ceebf
commit
c772ba3473
346
cmd/init.go
Normal file
346
cmd/init.go
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/comp500/packwiz/core"
|
||||||
|
"github.com/fatih/camelcase"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initCmd represents the init command
|
||||||
|
var initCmd = &cobra.Command{
|
||||||
|
Use: "init",
|
||||||
|
Short: "Initialise a packwiz modpack",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
_, err := os.Stat(viper.GetString("pack-file"))
|
||||||
|
if err == nil && !viper.GetBool("init.reinit") {
|
||||||
|
fmt.Println("Modpack metadata file already exists, use -r to override!")
|
||||||
|
os.Exit(1)
|
||||||
|
} else if err != nil && !os.IsNotExist(err) {
|
||||||
|
fmt.Printf("Error checking pack file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := cmd.Flags().GetString("name")
|
||||||
|
if err != nil || len(name) == 0 {
|
||||||
|
// Get current file directory name
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
directoryName := "."
|
||||||
|
if err == nil {
|
||||||
|
directoryName = filepath.Base(wd)
|
||||||
|
}
|
||||||
|
if directoryName != "." && len(directoryName) > 0 {
|
||||||
|
// Turn directory name into a space-seperated proper name
|
||||||
|
name = strings.ReplaceAll(strings.ReplaceAll(strings.Join(camelcase.Split(directoryName), " "), " - ", " "), " _ ", " ")
|
||||||
|
fmt.Print("Modpack name [" + name + "]: ")
|
||||||
|
} else {
|
||||||
|
fmt.Print("Modpack name: ")
|
||||||
|
}
|
||||||
|
readName, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading input: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Trims both CR and LF
|
||||||
|
readName = strings.TrimSpace(strings.TrimRight(readName, "\r\n"))
|
||||||
|
if len(readName) > 0 {
|
||||||
|
name = readName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mcVersions, err := getValidMCVersions()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to get latest minecraft versions: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
mcVersion := viper.GetString("init.mc-version")
|
||||||
|
if len(mcVersion) == 0 {
|
||||||
|
var latestVersion string
|
||||||
|
if viper.GetBool("init.snapshot") {
|
||||||
|
latestVersion = mcVersions.Latest.Snapshot
|
||||||
|
} else {
|
||||||
|
latestVersion = mcVersions.Latest.Release
|
||||||
|
}
|
||||||
|
if viper.GetBool("init.latest") {
|
||||||
|
mcVersion = latestVersion
|
||||||
|
} else {
|
||||||
|
fmt.Print("Minecraft version [" + latestVersion + "]: ")
|
||||||
|
mcVersion, err = bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading input: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Trims both CR and LF
|
||||||
|
mcVersion = strings.TrimSpace(strings.TrimRight(mcVersion, "\r\n"))
|
||||||
|
if len(mcVersion) == 0 {
|
||||||
|
mcVersion = latestVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mcVersions.checkValid(mcVersion)
|
||||||
|
|
||||||
|
// TODO: minecraft modloader
|
||||||
|
modLoaderName := viper.GetString("init.modloader")
|
||||||
|
if len(modLoaderName) == 0 {
|
||||||
|
var defaultLoader string
|
||||||
|
if viper.GetBool("init.snapshot") {
|
||||||
|
defaultLoader = "fabric"
|
||||||
|
} else {
|
||||||
|
defaultLoader = "forge"
|
||||||
|
}
|
||||||
|
fmt.Print("Mod loader [" + defaultLoader + "]: ")
|
||||||
|
modLoaderName, err = bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading input: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Trims both CR and LF
|
||||||
|
modLoaderName = strings.ToLower(strings.TrimSpace(strings.TrimRight(modLoaderName, "\r\n")))
|
||||||
|
if len(modLoaderName) == 0 {
|
||||||
|
modLoaderName = defaultLoader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, ok := modLoaders[modLoaderName]
|
||||||
|
if modLoaderName != "none" && !ok {
|
||||||
|
fmt.Println("Given mod loader is not supported! Use \"none\" to specify no modloader, or to configure one manually.")
|
||||||
|
fmt.Print("The following mod loaders are supported: ")
|
||||||
|
keys := make([]string, len(modLoaders))
|
||||||
|
i := 0
|
||||||
|
for k := range modLoaders {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
fmt.Println(strings.Join(keys, ", "))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var modLoaderVersion string
|
||||||
|
if modLoaderName != "none" {
|
||||||
|
versions, latestVersion, err := modLoaders[modLoaderName](mcVersion)
|
||||||
|
modLoaderVersion = viper.GetString("init.modloader-version")
|
||||||
|
if len(modLoaderVersion) == 0 {
|
||||||
|
if viper.GetBool("init.modloader-latest") {
|
||||||
|
modLoaderVersion = latestVersion
|
||||||
|
} else {
|
||||||
|
fmt.Print("Mod loader version [" + latestVersion + "]: ")
|
||||||
|
modLoaderVersion, err = bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading input: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Trims both CR and LF
|
||||||
|
modLoaderVersion = strings.ToLower(strings.TrimSpace(strings.TrimRight(modLoaderVersion, "\r\n")))
|
||||||
|
if len(modLoaderVersion) == 0 {
|
||||||
|
modLoaderVersion = latestVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, v := range versions {
|
||||||
|
if modLoaderVersion == v {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
fmt.Println("Given mod loader version cannot be found!")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indexFilePath := viper.GetString("init.index-file")
|
||||||
|
_, err = os.Stat(indexFilePath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Create file
|
||||||
|
err = ioutil.WriteFile(indexFilePath, []byte{}, 0644)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating index file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println(indexFilePath + " created!")
|
||||||
|
} else if err != nil {
|
||||||
|
fmt.Printf("Error checking index file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the pack
|
||||||
|
pack := core.Pack{
|
||||||
|
Name: name,
|
||||||
|
Index: struct {
|
||||||
|
File string `toml:"file"`
|
||||||
|
HashFormat string `toml:"hash-format"`
|
||||||
|
Hash string `toml:"hash"`
|
||||||
|
}{
|
||||||
|
File: indexFilePath,
|
||||||
|
},
|
||||||
|
Versions: map[string]string{
|
||||||
|
"minecraft": mcVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if modLoaderName != "none" {
|
||||||
|
pack.Versions[modLoaderName] = modLoaderVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the index and pack
|
||||||
|
index, err := pack.LoadIndex()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = index.Refresh()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = index.Write()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = pack.UpdateIndexHash()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = pack.Write()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println(viper.GetString("pack-file") + " created!")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(initCmd)
|
||||||
|
|
||||||
|
initCmd.Flags().String("name", "", "The name of the modpack (omit to define interactively)")
|
||||||
|
initCmd.Flags().String("index-file", "index.toml", "The index file to use")
|
||||||
|
viper.BindPFlag("init.index-file", initCmd.Flags().Lookup("index-file"))
|
||||||
|
initCmd.Flags().String("mc-version", "", "The version of Minecraft to use (omit to define interactively)")
|
||||||
|
viper.BindPFlag("init.mc-version", initCmd.Flags().Lookup("mc-version"))
|
||||||
|
initCmd.Flags().BoolP("latest", "l", false, "Automatically select the latest version of Minecraft")
|
||||||
|
viper.BindPFlag("init.latest", initCmd.Flags().Lookup("latest"))
|
||||||
|
initCmd.Flags().BoolP("snapshot", "s", false, "Use the latest snapshot version with --latest")
|
||||||
|
viper.BindPFlag("init.snapshot", initCmd.Flags().Lookup("snapshot"))
|
||||||
|
initCmd.Flags().BoolP("reinit", "r", false, "Recreate the pack file if it already exists, rather than exiting")
|
||||||
|
viper.BindPFlag("init.reinit", initCmd.Flags().Lookup("reinit"))
|
||||||
|
initCmd.Flags().String("modloader", "", "The mod loader to use (omit to define interactively)")
|
||||||
|
viper.BindPFlag("init.modloader", initCmd.Flags().Lookup("modloader"))
|
||||||
|
initCmd.Flags().String("modloader-version", "", "The mod loader version to use (omit to define interactively)")
|
||||||
|
viper.BindPFlag("init.modloader-version", initCmd.Flags().Lookup("modloader-version"))
|
||||||
|
initCmd.Flags().BoolP("modloader-latest", "L", false, "Automatically select the latest version of the mod loader")
|
||||||
|
viper.BindPFlag("init.modloader-latest", initCmd.Flags().Lookup("modloader-latest"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mcVersionManifest struct {
|
||||||
|
Latest struct {
|
||||||
|
Release string `json:"release"`
|
||||||
|
Snapshot string `json:"snapshot"`
|
||||||
|
} `json:"latest"`
|
||||||
|
Versions []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
ReleaseTime time.Time `json:"releaseTime"`
|
||||||
|
} `json:"versions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mcVersionManifest) checkValid(version string) {
|
||||||
|
for _, v := range m.Versions {
|
||||||
|
if v.ID == version {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Given version is not a valid Minecraft version!")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValidMCVersions() (mcVersionManifest, error) {
|
||||||
|
res, err := http.Get("https://launchermeta.mojang.com/mc/game/version_manifest.json")
|
||||||
|
if err != nil {
|
||||||
|
return mcVersionManifest{}, err
|
||||||
|
}
|
||||||
|
dec := json.NewDecoder(res.Body)
|
||||||
|
out := mcVersionManifest{}
|
||||||
|
err = dec.Decode(&out)
|
||||||
|
if err != nil {
|
||||||
|
return mcVersionManifest{}, err
|
||||||
|
}
|
||||||
|
// Sort by newest to oldest
|
||||||
|
sort.Slice(out.Versions, func(i, j int) bool {
|
||||||
|
return out.Versions[i].ReleaseTime.Before(out.Versions[j].ReleaseTime)
|
||||||
|
})
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mavenMetadata struct {
|
||||||
|
XMLName xml.Name `xml:"metadata"`
|
||||||
|
GroupID string `xml:"groupId"`
|
||||||
|
ArtifactID string `xml:"artifactId"`
|
||||||
|
Versioning struct {
|
||||||
|
Release string `xml:"release"`
|
||||||
|
Versions struct {
|
||||||
|
Version []string `xml:"version"`
|
||||||
|
} `xml:"versions"`
|
||||||
|
LastUpdated string `xml:"lastUpdated"`
|
||||||
|
} `xml:"versioning"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets a list of modloader versions and latest version for a given Minecraft version
|
||||||
|
var modLoaders = map[string]func(mcVersion string) ([]string, string, error){
|
||||||
|
"fabric": func(mcVersion string) ([]string, string, error) {
|
||||||
|
res, err := http.Get("https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml")
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, "", err
|
||||||
|
}
|
||||||
|
dec := xml.NewDecoder(res.Body)
|
||||||
|
out := mavenMetadata{}
|
||||||
|
err = dec.Decode(&out)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, "", err
|
||||||
|
}
|
||||||
|
return out.Versioning.Versions.Version, out.Versioning.Release, nil
|
||||||
|
},
|
||||||
|
"forge": func(mcVersion string) ([]string, string, error) {
|
||||||
|
res, err := http.Get("https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml")
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, "", err
|
||||||
|
}
|
||||||
|
dec := xml.NewDecoder(res.Body)
|
||||||
|
out := mavenMetadata{}
|
||||||
|
err = dec.Decode(&out)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, "", err
|
||||||
|
}
|
||||||
|
allowedVersions := make([]string, 0, len(out.Versioning.Versions.Version))
|
||||||
|
for _, v := range out.Versioning.Versions.Version {
|
||||||
|
if strings.HasPrefix(v, mcVersion) {
|
||||||
|
allowedVersions = append(allowedVersions, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(allowedVersions) == 0 {
|
||||||
|
return []string{}, "", errors.New("no Forge versions available for this Minecraft version")
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(out.Versioning.Release, mcVersion) {
|
||||||
|
return allowedVersions, out.Versioning.Release, nil
|
||||||
|
}
|
||||||
|
return allowedVersions, allowedVersions[len(allowedVersions)-1], nil
|
||||||
|
},
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
|||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
||||||
github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect
|
github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect
|
||||||
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817
|
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817
|
||||||
|
github.com/fatih/camelcase v1.0.0
|
||||||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 // indirect
|
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 // indirect
|
||||||
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 // indirect
|
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 // indirect
|
||||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e // indirect
|
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -30,6 +30,8 @@ github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817 h1:0nsrg//Dc
|
|||||||
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817/go.mod h1:C/+sI4IFnEpCn6VQ3GIPEp+FrQnQw+YQP3+n+GdGq7o=
|
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817/go.mod h1:C/+sI4IFnEpCn6VQ3GIPEp+FrQnQw+YQP3+n+GdGq7o=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user