1
0
Fork 0
mccl/cmd/mccl/commands/install_command.go

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
}