326 lines
8.1 KiB
Go
Executable File
326 lines
8.1 KiB
Go
Executable File
package commands
|
|
|
|
import (
|
|
"archive/zip"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mccl/internal/assets"
|
|
"mccl/internal/manifest"
|
|
"mccl/internal/version_manifest"
|
|
"mccl/pkg/retriever"
|
|
"mccl/pkg/util"
|
|
"os"
|
|
"path"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type InstallCommand struct {
|
|
Id string
|
|
DestDir string
|
|
}
|
|
|
|
func NewInstallCommand(id, destDir string) *InstallCommand {
|
|
return &InstallCommand{Id: id, DestDir: destDir}
|
|
}
|
|
|
|
func (ic *InstallCommand) Run() error {
|
|
if ic.Id == "" {
|
|
return errors.New("an empty Minecraft version was provided")
|
|
}
|
|
if ic.DestDir == "" {
|
|
return errors.New("an empty path was provided")
|
|
}
|
|
return ic.install_vanilla_client()
|
|
}
|
|
|
|
func (ic *InstallCommand) install_vanilla_client() error {
|
|
var err error
|
|
var data []byte
|
|
|
|
if ic.DestDir == "." {
|
|
execPath, _ := os.Executable()
|
|
ic.DestDir = path.Dir(execPath)
|
|
}
|
|
|
|
if _, err := os.Stat(ic.DestDir); err != nil {
|
|
if err := os.Mkdir(ic.DestDir, 0777); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
fmt.Print("Retrieving a version_manifest_v2.json... ")
|
|
data, err = util.GetFromUrl(version_manifest.VersionManifestUrl, "", "", -1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
versionManifest, err := version_manifest.New(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
|
|
var version version_manifest.Version
|
|
if ic.Id == "release" || ic.Id == "latest-release" {
|
|
version, err = versionManifest.GetLatest(version_manifest.VersionTypeRelease)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if ic.Id == "snapshot" || ic.Id == "latest-snapshot" {
|
|
version, err = versionManifest.GetLatest(version_manifest.VersionTypeSnapshot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
version, err = versionManifest.Get(ic.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
fmt.Printf("Version %s (%s) was chosen.\n", version.Id, version.Type)
|
|
|
|
fmt.Print("Retrieving a manifest file... ")
|
|
manifestPath := path.Join(ic.DestDir, "versions", version.Id, version.Id+".json")
|
|
data, err = util.LoadOrDownloadFile(manifestPath, version.Url, version.Sha1, "sha1", -1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
manifst, err := manifest.New(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
|
|
if !util.IsFileExist(manifestPath) {
|
|
fmt.Print("Writing a manifest file to ", manifestPath, " ... ")
|
|
if err := util.WriteFile(manifestPath, data); err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
} else {
|
|
fmt.Println(manifestPath, "does exist. Skipping")
|
|
}
|
|
|
|
clientPath := path.Join(ic.DestDir, "versions", manifst.Id, manifst.Id+".jar")
|
|
if !util.IsFileExist(clientPath) {
|
|
fmt.Print("Retrieving and writing a client to ", clientPath, " ... ")
|
|
data, err = util.LoadOrDownloadFile(clientPath, manifst.Downloads["client"].Url,
|
|
manifst.Downloads["client"].Sha1, "sha1", manifst.Downloads["client"].Size)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteFile(clientPath, data); err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
} else {
|
|
fmt.Println(clientPath, "does exist. Skipping")
|
|
}
|
|
|
|
fmt.Printf("Retrieving an asset index %s.json... ", manifst.AssetIndex.Id)
|
|
assetIndexPath := path.Join(ic.DestDir, "assets", "indexes", manifst.AssetIndex.Id+".json")
|
|
|
|
data, err = util.LoadOrDownloadFile(assetIndexPath, manifst.AssetIndex.Url,
|
|
manifst.AssetIndex.Sha1, "sha1", manifst.AssetIndex.Size)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
asts, err := assets.New(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
|
|
if !util.IsFileExist(assetIndexPath) {
|
|
fmt.Printf("Writing an asset index %s to %s...", manifst.AssetIndex.Id, assetIndexPath)
|
|
if err := util.WriteFile(assetIndexPath, data); err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
} else {
|
|
fmt.Printf("%s does exist. Skip writing\n", assetIndexPath)
|
|
}
|
|
|
|
fmt.Print("Retrieving assets...")
|
|
|
|
assetsDir := path.Join(ic.DestDir, "assets", "objects")
|
|
ar := retriever.New()
|
|
defer ar.Close()
|
|
|
|
assetsCount := len(asts.Objects)
|
|
var assetItems []retriever.Retrievable
|
|
|
|
for _, v := range asts.Objects {
|
|
assetItems = append(assetItems, v)
|
|
}
|
|
|
|
go func() {
|
|
if err := ar.Run(assetItems, assets.RetrieveAsset, assetsDir); err != nil {
|
|
fmt.Println("\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
for written, cnt := 0, 0; cnt < assetsCount; {
|
|
if len(ar.GetErrorChan()) > 0 {
|
|
return <-ar.GetErrorChan()
|
|
}
|
|
|
|
if cnt < assetsCount {
|
|
written += <-ar.GetDoneChan()
|
|
}
|
|
cnt++
|
|
|
|
fmt.Printf("\rRetrieving assets... %d/%d (%d/%d)", written, manifst.AssetIndex.TotalSize, cnt, assetsCount)
|
|
}
|
|
fmt.Printf("\nRetrieving assets... Done\n")
|
|
|
|
if manifst.Logging.Client.Argument != "" {
|
|
logConfPath := path.Join(ic.DestDir, "assets", "log_configs", manifst.Logging.Client.File.Id)
|
|
if !util.IsFileExist(logConfPath) {
|
|
fmt.Printf("Retrieving and writing %s...", logConfPath)
|
|
data, err = util.LoadOrDownloadFile(logConfPath, manifst.Logging.Client.File.Url,
|
|
manifst.Logging.Client.File.Sha1, "sha1", manifst.Logging.Client.File.Size)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := util.WriteFile(logConfPath, data); err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Done")
|
|
} else {
|
|
fmt.Printf("%s does exist. Skip writing\n", logConfPath)
|
|
}
|
|
}
|
|
|
|
fmt.Print("Retrieving libraries...")
|
|
|
|
librariesDir := path.Join(ic.DestDir, "libraries")
|
|
lr := retriever.New()
|
|
defer lr.Close()
|
|
|
|
var libraryItems []retriever.Retrievable
|
|
var totalLibrariesSize int64 = 0
|
|
|
|
for _, lib := range manifst.Libraries {
|
|
if lib.CheckRules() {
|
|
files := lib.ToFile()
|
|
|
|
for _, f := range files {
|
|
totalLibrariesSize += f.Size
|
|
libraryItems = append(libraryItems, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
if err := lr.Run(libraryItems, manifest.RetrieveLibrary, librariesDir); err != nil {
|
|
fmt.Println("\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
librariesCount := len(libraryItems)
|
|
|
|
for written, cnt := 0, 0; cnt < librariesCount; {
|
|
if len(lr.GetErrorChan()) > 0 {
|
|
return <-lr.GetErrorChan()
|
|
}
|
|
|
|
if cnt < librariesCount {
|
|
written += <-lr.GetDoneChan()
|
|
}
|
|
cnt++
|
|
|
|
fmt.Printf("\rRetrieving libraries... %d/%d (%d/%d)",
|
|
written, totalLibrariesSize, cnt, librariesCount)
|
|
}
|
|
|
|
fmt.Printf("\nRetrieving libraries... Done\n")
|
|
|
|
releaseTime, err := time.Parse(time.RFC3339, manifst.ReleaseTime)
|
|
if err != nil {
|
|
releaseTime, err = time.Parse("2006-01-02T15:04:05Z0700", manifst.ReleaseTime)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
firstVerWithNoExtract, _ := time.Parse(time.DateOnly, "2017-10-25")
|
|
if releaseTime.Before(firstVerWithNoExtract) {
|
|
nativesDir := path.Join(ic.DestDir, "versions", manifst.Id, "natives")
|
|
|
|
if err := os.MkdirAll(nativesDir, 0777); err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Extracting native libraries to %s ... ", nativesDir)
|
|
for _, lib := range libraryItems {
|
|
l := lib.(util.File)
|
|
|
|
if !strings.Contains(l.GetName(), "natives") {
|
|
continue
|
|
}
|
|
|
|
zipFile, err := zip.OpenReader(path.Join(librariesDir, l.Path))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zipFile.Close()
|
|
|
|
for _, f := range zipFile.File {
|
|
if strings.HasPrefix(f.Name, "META-INF/") {
|
|
continue
|
|
}
|
|
|
|
if strings.Contains(f.Name, "64") && runtime.GOARCH != "amd64" {
|
|
continue
|
|
}
|
|
|
|
switch runtime.GOARCH {
|
|
case "386":
|
|
if strings.Contains(f.Name, "64") {
|
|
continue
|
|
}
|
|
case "amd64":
|
|
if !strings.Contains(f.Name, "64") {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if util.IsFileExist(path.Join(nativesDir, f.Name)) {
|
|
continue
|
|
}
|
|
|
|
frc, err := f.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer frc.Close()
|
|
|
|
out, err := os.OpenFile(path.Join(nativesDir, f.Name), os.O_CREATE|os.O_RDWR, 0666)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
written, err := io.Copy(out, frc)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract file %s: %s", f.Name, err)
|
|
} else if written != int64(f.UncompressedSize64) {
|
|
return fmt.Errorf("failed to extract file %s: size is %d, but %d was written",
|
|
f.Name, f.UncompressedSize64, written)
|
|
}
|
|
}
|
|
}
|
|
fmt.Println("Done")
|
|
}
|
|
|
|
return nil
|
|
}
|