mirror of
				https://github.com/packwiz/packwiz.git
				synced 2025-10-31 02:34:31 +01: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
 | |
| }
 |