mirror of
https://github.com/packwiz/packwiz.git
synced 2025-04-19 21:16:30 +02:00
189 lines
5.2 KiB
Go
189 lines
5.2 KiB
Go
package core
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
"github.com/Masterminds/semver/v3"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// Pack stores the modpack metadata, usually in pack.toml
|
|
type Pack struct {
|
|
Name string `toml:"name"`
|
|
Author string `toml:"author,omitempty"`
|
|
Version string `toml:"version,omitempty"`
|
|
Description string `toml:"description,omitempty"`
|
|
PackFormat string `toml:"pack-format"`
|
|
Index struct {
|
|
// Path is stored in forward slash format relative to pack.toml
|
|
File string `toml:"file"`
|
|
HashFormat string `toml:"hash-format"`
|
|
Hash string `toml:"hash,omitempty"`
|
|
} `toml:"index"`
|
|
Versions map[string]string `toml:"versions"`
|
|
Export map[string]map[string]interface{} `toml:"export"`
|
|
Options map[string]interface{} `toml:"options"`
|
|
}
|
|
|
|
const CurrentPackFormat = "packwiz:1.1.0"
|
|
|
|
var PackFormatConstraintAccepted = mustParseConstraint("~1")
|
|
var PackFormatConstraintSuggestUpgrade = mustParseConstraint("~1.1")
|
|
|
|
func mustParseConstraint(s string) *semver.Constraints {
|
|
c, err := semver.NewConstraint(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
// LoadPack loads the modpack metadata to a Pack struct
|
|
func LoadPack() (Pack, error) {
|
|
var modpack Pack
|
|
if _, err := toml.DecodeFile(viper.GetString("pack-file"), &modpack); err != nil {
|
|
return Pack{}, err
|
|
}
|
|
|
|
// Check pack-format
|
|
if len(modpack.PackFormat) == 0 {
|
|
fmt.Println("Modpack manifest has no pack-format field; assuming packwiz:1.1.0")
|
|
modpack.PackFormat = "packwiz:1.1.0"
|
|
}
|
|
// Auto-migrate versions
|
|
if modpack.PackFormat == "packwiz:1.0.0" {
|
|
fmt.Println("Automatically migrating pack to packwiz:1.1.0 format...")
|
|
modpack.PackFormat = "packwiz:1.1.0"
|
|
}
|
|
if !strings.HasPrefix(modpack.PackFormat, "packwiz:") {
|
|
return Pack{}, errors.New("pack-format field does not indicate a valid packwiz pack")
|
|
}
|
|
ver, err := semver.StrictNewVersion(strings.TrimPrefix(modpack.PackFormat, "packwiz:"))
|
|
if err != nil {
|
|
return Pack{}, fmt.Errorf("pack-format field is not valid semver: %w", err)
|
|
}
|
|
if !PackFormatConstraintAccepted.Check(ver) {
|
|
return Pack{}, errors.New("the modpack is incompatible with this version of packwiz; please update")
|
|
}
|
|
if !PackFormatConstraintSuggestUpgrade.Check(ver) {
|
|
fmt.Println("Modpack has a newer feature number than is supported by this version of packwiz. Update to the latest version of packwiz for new features and bugfixes!")
|
|
}
|
|
// TODO: suggest migration if necessary (primarily for 2.0.0)
|
|
|
|
// Read options into viper
|
|
if modpack.Options != nil {
|
|
err := viper.MergeConfigMap(modpack.Options)
|
|
if err != nil {
|
|
return Pack{}, err
|
|
}
|
|
}
|
|
|
|
if len(modpack.Index.File) == 0 {
|
|
modpack.Index.File = "index.toml"
|
|
}
|
|
return modpack, nil
|
|
}
|
|
|
|
// LoadIndex attempts to load the index file of this modpack
|
|
func (pack Pack) LoadIndex() (Index, error) {
|
|
if filepath.IsAbs(pack.Index.File) {
|
|
return LoadIndex(pack.Index.File)
|
|
}
|
|
fileNative := filepath.FromSlash(pack.Index.File)
|
|
return LoadIndex(filepath.Join(filepath.Dir(viper.GetString("pack-file")), fileNative))
|
|
}
|
|
|
|
// UpdateIndexHash recalculates the hash of the index file of this modpack
|
|
func (pack *Pack) UpdateIndexHash() error {
|
|
if viper.GetBool("no-internal-hashes") {
|
|
pack.Index.HashFormat = "sha256"
|
|
pack.Index.Hash = ""
|
|
return nil
|
|
}
|
|
|
|
fileNative := filepath.FromSlash(pack.Index.File)
|
|
indexFile := filepath.Join(filepath.Dir(viper.GetString("pack-file")), fileNative)
|
|
if filepath.IsAbs(pack.Index.File) {
|
|
indexFile = pack.Index.File
|
|
}
|
|
|
|
f, err := os.Open(indexFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Hash usage strategy (may change):
|
|
// Just use SHA256, overwrite existing hash regardless of what it is
|
|
// May update later to continue using the same hash that was already being used
|
|
h, err := GetHashImpl("sha256")
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return err
|
|
}
|
|
if _, err := io.Copy(h, f); err != nil {
|
|
_ = f.Close()
|
|
return err
|
|
}
|
|
hashString := h.HashToString(h.Sum(nil))
|
|
|
|
pack.Index.HashFormat = "sha256"
|
|
pack.Index.Hash = hashString
|
|
return f.Close()
|
|
}
|
|
|
|
// Write saves the pack file
|
|
func (pack Pack) Write() error {
|
|
f, err := os.Create(viper.GetString("pack-file"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
enc := toml.NewEncoder(f)
|
|
// Disable indentation
|
|
enc.Indent = ""
|
|
err = enc.Encode(pack)
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return err
|
|
}
|
|
return f.Close()
|
|
}
|
|
|
|
// GetMCVersion gets the version of Minecraft this pack uses, if it has been correctly specified
|
|
func (pack Pack) GetMCVersion() (string, error) {
|
|
mcVersion, ok := pack.Versions["minecraft"]
|
|
if !ok {
|
|
return "", errors.New("no minecraft version specified in modpack")
|
|
}
|
|
return mcVersion, nil
|
|
}
|
|
|
|
func (pack Pack) GetPackName() string {
|
|
if pack.Name == "" {
|
|
return "export"
|
|
} else if pack.Version == "" {
|
|
return pack.Name
|
|
} else {
|
|
return pack.Name + "-" + pack.Version
|
|
}
|
|
}
|
|
|
|
func (pack Pack) GetLoaders() (loaders []string) {
|
|
if _, hasQuilt := pack.Versions["quilt"]; hasQuilt {
|
|
loaders = append(loaders, "quilt")
|
|
loaders = append(loaders, "fabric") // Backwards-compatible; for now (could be configurable later)
|
|
} else if _, hasFabric := pack.Versions["fabric"]; hasFabric {
|
|
loaders = append(loaders, "fabric")
|
|
}
|
|
if _, hasForge := pack.Versions["forge"]; hasForge {
|
|
loaders = append(loaders, "forge")
|
|
}
|
|
return
|
|
}
|