2024-10-29 21:48:59 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-10-30 15:39:11 -06:00
|
|
|
"bytes"
|
2024-10-29 21:48:59 -06:00
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-10-30 15:39:11 -06:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
2024-10-29 21:48:59 -06:00
|
|
|
"time"
|
2024-10-30 15:39:11 -06:00
|
|
|
|
|
|
|
wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
|
|
|
"github.com/zhyee/zipstream"
|
2024-10-29 21:48:59 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type McVersionManifestEntry struct {
|
|
|
|
Id string
|
|
|
|
Type string
|
|
|
|
Url string
|
|
|
|
Time time.Time
|
|
|
|
ReleaseTime time.Time
|
|
|
|
Sha1 string
|
|
|
|
ComplianceLevel int
|
|
|
|
}
|
|
|
|
|
|
|
|
type McLatestEntry struct {
|
|
|
|
Release string
|
|
|
|
Snapshot string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McVersionManifest struct {
|
|
|
|
Latest McLatestEntry
|
|
|
|
Versions []McVersionManifestEntry
|
|
|
|
}
|
|
|
|
|
|
|
|
type McArguments struct {
|
|
|
|
Game []string
|
|
|
|
Jvm []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McAssetIndex struct {
|
|
|
|
Id string
|
|
|
|
Sha1 string
|
|
|
|
Size int
|
|
|
|
TotalSize int
|
|
|
|
Url string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McJavaVersion struct {
|
|
|
|
Component string
|
|
|
|
MajorVersion int
|
|
|
|
}
|
|
|
|
|
|
|
|
type McLibraryArtifact struct {
|
|
|
|
Path string
|
|
|
|
Sha1 string
|
|
|
|
Size int
|
|
|
|
Url string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McLibraryDownload struct {
|
|
|
|
Artifact McLibraryArtifact
|
2024-10-30 15:39:11 -06:00
|
|
|
Classifiers map[string]interface{}
|
2024-10-29 21:48:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type McRuleOs struct {
|
|
|
|
Name string
|
|
|
|
Version string
|
|
|
|
ARch string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McRule struct {
|
|
|
|
Action string
|
|
|
|
Features map[string]bool
|
|
|
|
Os McRuleOs
|
|
|
|
}
|
|
|
|
|
|
|
|
type McLibrary struct {
|
|
|
|
Downloads McLibraryDownload
|
|
|
|
Name string
|
|
|
|
Rules []McRule
|
2024-10-30 15:39:11 -06:00
|
|
|
Natives map[string]string
|
2024-10-29 21:48:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type McDownload struct {
|
|
|
|
Sha1 string
|
|
|
|
Size int
|
|
|
|
Url string
|
|
|
|
}
|
|
|
|
|
2024-10-30 15:58:05 -06:00
|
|
|
type McLogging struct {
|
|
|
|
Client McLoggingClient
|
|
|
|
}
|
|
|
|
|
|
|
|
type McLoggingClient struct {
|
|
|
|
Argument string
|
|
|
|
File McDownload
|
|
|
|
}
|
|
|
|
|
2024-10-29 21:48:59 -06:00
|
|
|
type McMetadata struct {
|
|
|
|
Arguments McArguments
|
|
|
|
AssetIndex McAssetIndex
|
|
|
|
Assets string
|
|
|
|
ComplianceLevel int
|
|
|
|
Downloads map[string]McDownload
|
|
|
|
Id string
|
|
|
|
JavaVersion McJavaVersion
|
|
|
|
Libraries []McLibrary
|
|
|
|
MainClass string
|
|
|
|
MinimumLauncherVersion int
|
|
|
|
ReleaseTime time.Time
|
|
|
|
Time time.Time
|
|
|
|
Type string
|
2024-10-30 15:39:11 -06:00
|
|
|
MinecraftArguments string
|
2024-10-30 15:58:05 -06:00
|
|
|
Logging McLogging
|
2024-10-29 21:48:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func GetVersionManifest() (McVersionManifest, error) {
|
|
|
|
resp, err := http.Get("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json")
|
|
|
|
var returnError error = nil
|
|
|
|
if err == nil {
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
|
|
if err == nil {
|
|
|
|
versionManifest := McVersionManifest{}
|
|
|
|
err = json.Unmarshal(data, &versionManifest)
|
|
|
|
if err == nil {
|
|
|
|
dir, err := os.UserConfigDir()
|
|
|
|
if err != nil {
|
|
|
|
return versionManifest, nil
|
|
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(dir, "FCLauncher", "cache"), 0755)
|
|
|
|
f, err := os.OpenFile(filepath.Join(dir, "FCLauncher", "cache", "versionManifest.json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
return versionManifest, nil
|
|
|
|
} else {
|
|
|
|
returnError = fmt.Errorf("Unable to parse Json: %e\n", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
returnError = fmt.Errorf("Unable to read version manifest: %e\n", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
returnError = fmt.Errorf("Unable to download version manifest: %e\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dir, err := os.UserConfigDir()
|
|
|
|
if err != nil {
|
|
|
|
return McVersionManifest{}, returnError
|
|
|
|
}
|
|
|
|
path := filepath.Join(dir, "FCLauncher", "cache", "versionManifest.json")
|
|
|
|
if _, err = os.Stat(path); err != nil {
|
|
|
|
return McVersionManifest{}, returnError
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(path, os.O_RDONLY, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return McVersionManifest{}, returnError
|
|
|
|
}
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
if err != nil {
|
|
|
|
return McVersionManifest{}, returnError
|
|
|
|
}
|
|
|
|
versionManifest := McVersionManifest{}
|
|
|
|
err = json.Unmarshal(data, &versionManifest)
|
|
|
|
if err != nil {
|
|
|
|
return McVersionManifest{}, returnError
|
|
|
|
}
|
|
|
|
return versionManifest, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetVersionMetadata(wantedVersion string) (McMetadata, error) {
|
|
|
|
manifest, err := GetVersionManifest()
|
|
|
|
if err != nil {
|
|
|
|
return McMetadata{}, fmt.Errorf("GetVersionMetadata: %e\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, version := range manifest.Versions {
|
|
|
|
if wantedVersion == version.Id {
|
|
|
|
//found it
|
|
|
|
dir, err := os.UserConfigDir()
|
|
|
|
if err == nil {
|
|
|
|
path := filepath.Join(dir, "FCLauncher", "cache", "versionMetadata", wantedVersion+".json")
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
if f, err := os.OpenFile(path, os.O_RDONLY, 0755); err == nil {
|
|
|
|
defer f.Close()
|
|
|
|
if data, err := io.ReadAll(f); err == nil {
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == version.Sha1{
|
|
|
|
metadata := McMetadata{}
|
|
|
|
json.Unmarshal(data, &metadata)
|
|
|
|
return metadata, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resp, err := http.Get(version.Url)
|
|
|
|
if err != nil {
|
|
|
|
return McMetadata{}, fmt.Errorf("Unable to download metadata: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return McMetadata{}, fmt.Errorf("Unable to download metadata: %e\n", err)
|
|
|
|
}
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) != version.Sha1 {
|
|
|
|
return McMetadata{}, fmt.Errorf("GetMetadata: Sha1 hash does not match\n")
|
|
|
|
}
|
|
|
|
metadata := McMetadata{}
|
|
|
|
err = json.Unmarshal(data, &metadata)
|
|
|
|
dir, err = os.UserConfigDir()
|
|
|
|
if err == nil {
|
|
|
|
path := filepath.Join(dir, "FCLauncher", "cache", "versionMetadata")
|
|
|
|
if os.MkdirAll(path, 0755) == nil {
|
|
|
|
if f, err := os.OpenFile(filepath.Join(path, wantedVersion+".json"), os.O_CREATE|os.O_RDWR, 0755); err == nil {
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return metadata, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return McMetadata{}, fmt.Errorf("Unable to find version %s\n", wantedVersion)
|
|
|
|
}
|
2024-10-30 15:39:11 -06:00
|
|
|
|
|
|
|
func GetAssetIndex(mcVersion string) ([]string, error) {
|
|
|
|
found := false
|
|
|
|
var data []byte
|
|
|
|
metadata, err := GetVersionMetadata(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, fmt.Errorf("Unable to pull manifest: %e\n", err)
|
|
|
|
}
|
|
|
|
dir, _ := os.UserConfigDir()
|
|
|
|
path := filepath.Join(dir, "FCLauncher", "assets", "indexes")
|
|
|
|
if _, err := os.Stat(filepath.Join(path, metadata.Assets+".json")); err == nil {
|
|
|
|
//cache file exists
|
|
|
|
if f, err := os.OpenFile(filepath.Join(path, metadata.Assets+".json"), os.O_RDONLY, 0755); err == nil {
|
|
|
|
defer f.Close()
|
|
|
|
if data, err = io.ReadAll(f); err == nil {
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == metadata.AssetIndex.Sha1 {
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
//no cache file
|
|
|
|
data = []byte{}
|
|
|
|
resp, err := http.Get(metadata.AssetIndex.Url)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, fmt.Errorf("Unable to pull asset index: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, err = io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, fmt.Errorf("Unable to pull asset index: %e\n", err)
|
|
|
|
}
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) != metadata.AssetIndex.Sha1 {
|
|
|
|
return []string{}, fmt.Errorf("Sha mismatch!\n")
|
|
|
|
}
|
|
|
|
os.MkdirAll(path, 0755)
|
|
|
|
f, _ := os.OpenFile(filepath.Join(path, metadata.Assets+".json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
//at this point data is populated
|
|
|
|
var index map[string]interface{}
|
|
|
|
json.Unmarshal(data, &index)
|
|
|
|
index = index["objects"].(map[string]interface{})
|
|
|
|
hashes := []string{}
|
|
|
|
for _, val := range index {
|
|
|
|
index_map := val.(map[string]interface{})
|
|
|
|
hashes = append(hashes, index_map["hash"].(string))
|
|
|
|
}
|
|
|
|
return hashes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func DownloadAssets(mcVersion string, assetPath string, a App) error {
|
|
|
|
a.Status("Downloading Minecraft Assets")
|
|
|
|
assets, err := GetAssetIndex(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to get asset index: %e\n", err)
|
|
|
|
}
|
|
|
|
metadata, err := GetVersionMetadata(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to get version metadata: %e\n", err)
|
|
|
|
}
|
|
|
|
total := metadata.AssetIndex.TotalSize
|
|
|
|
downloaded := 0
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, total)
|
|
|
|
for _, hash := range assets {
|
|
|
|
if _, err := os.Stat(filepath.Join(assetPath, "objects", hash[:2], hash)); err == nil {
|
|
|
|
f, _ := os.OpenFile(filepath.Join(assetPath, "objects", hash[:2], hash), os.O_RDONLY, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == hash {
|
|
|
|
downloaded += len(data)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, total)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resp, err := http.Get(fmt.Sprintf("https://resources.download.minecraft.net/%s/%s", hash[:2], hash))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download assets: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
buff := new(bytes.Buffer)
|
|
|
|
for {
|
|
|
|
count, err := io.CopyN(buff, resp.Body, BlockSize)
|
|
|
|
if err == io.EOF {
|
|
|
|
downloaded += int(count)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error Downloading assets: %e\n", err)
|
|
|
|
}
|
|
|
|
downloaded += int(count)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, total)
|
|
|
|
}
|
|
|
|
data := buff.Bytes()
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) != hash {
|
|
|
|
return fmt.Errorf("unable to download assets: Sha1 Mismatch\n")
|
|
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(assetPath, "objects", hash[:2]), 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download assets: Unable to create directory\n")
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(filepath.Join(assetPath, "objects", hash[:2], hash), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download assets: Unable to open file\n")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, total)
|
|
|
|
}
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download_complete")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func DownloadLibraries(mcVersion string, libPath string, a App) error {
|
|
|
|
metadata, err := GetVersionMetadata(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to pull version metadata: %e\n", err)
|
|
|
|
}
|
|
|
|
for _, lib := range metadata.Libraries {
|
|
|
|
a.Status(fmt.Sprintf("Checking %s\n", lib.Name))
|
|
|
|
if _, err := os.Stat(filepath.Join(libPath, lib.Downloads.Artifact.Path)); err == nil {
|
|
|
|
f, _ := os.OpenFile(filepath.Join(libPath, lib.Downloads.Artifact.Path), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == lib.Downloads.Artifact.Sha1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.Status(fmt.Sprintf("Downloading %s\n", lib.Name))
|
|
|
|
resp, err := http.Get(lib.Downloads.Artifact.Url)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download libs: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
buff := new(bytes.Buffer)
|
|
|
|
downloaded := 0
|
|
|
|
for {
|
|
|
|
count, err := io.CopyN(buff, resp.Body, BlockSize)
|
|
|
|
if err == io.EOF {
|
|
|
|
downloaded += int(count)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error Downloading libs: %e\n", err)
|
|
|
|
}
|
|
|
|
downloaded += int(count)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, resp.ContentLength)
|
|
|
|
}
|
|
|
|
data := buff.Bytes()
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) != lib.Downloads.Artifact.Sha1 {
|
|
|
|
return fmt.Errorf("unable to download libs: Sha1 Mismatch\n")
|
|
|
|
}
|
|
|
|
path := ""
|
|
|
|
tokens := strings.Split(lib.Downloads.Artifact.Path, "/")
|
|
|
|
for ind, token := range tokens {
|
|
|
|
if ind != len(tokens)-1 {
|
|
|
|
path = filepath.Join(path, token)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(libPath, path), 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download libs: Unable to create directory\n")
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(filepath.Join(libPath, lib.Downloads.Artifact.Path), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download libs: Unable to open file\n")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download_complete")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func InstallNatives(mcVersion string, nativesDir string){
|
|
|
|
metadata, _ := GetVersionMetadata(mcVersion)
|
|
|
|
for _, lib := range metadata.Libraries {
|
|
|
|
if lib.Natives != nil {
|
|
|
|
glob := lib.Natives[runtime.GOOS]
|
|
|
|
fmt.Printf("glob is: %s\n", glob)
|
|
|
|
if lib.Downloads.Classifiers[glob] != nil {
|
|
|
|
artifact := lib.Downloads.Classifiers[glob].(map[string]interface{})
|
|
|
|
resp, _ := http.Get(artifact["url"].(string))
|
|
|
|
defer resp.Body.Close()
|
|
|
|
zr := zipstream.NewReader(resp.Body)
|
|
|
|
for {
|
|
|
|
e, err := zr.GetNextEntry()
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if e.IsDir() {
|
|
|
|
os.MkdirAll(filepath.Join(nativesDir, e.Name), 0755)
|
|
|
|
} else {
|
|
|
|
zc, _ := e.Open()
|
|
|
|
f, _ := os.OpenFile(filepath.Join(nativesDir, e.Name), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer zc.Close()
|
|
|
|
defer f.Close()
|
|
|
|
io.Copy(f, zc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 15:58:05 -06:00
|
|
|
func DownloadLoggingConfig(mcVersion string, gameDir string){
|
|
|
|
metadata, _ := GetVersionMetadata(mcVersion)
|
|
|
|
resp, err := http.Get(metadata.Logging.Client.File.Url)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Error downloading logging config: %s\n", err)
|
2024-10-30 19:11:24 -06:00
|
|
|
return
|
2024-10-30 15:58:05 -06:00
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
os.MkdirAll(gameDir, 0755)
|
|
|
|
f, _ := os.OpenFile(filepath.Join(gameDir, "log4j2.xml"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
io.Copy(f, resp.Body)
|
|
|
|
}
|
|
|
|
|
2024-10-30 15:39:11 -06:00
|
|
|
func DownloadExecutable(mcVersion string, binDir string, a App) error {
|
|
|
|
metadata, err := GetVersionMetadata(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to pull version metadata: %e\n", err)
|
|
|
|
}
|
|
|
|
a.Status(fmt.Sprintf("Checking Minecraft %s Executable\n", mcVersion))
|
|
|
|
if _, err := os.Stat(filepath.Join(binDir, mcVersion, "client.jar")); err == nil {
|
|
|
|
f, _ := os.OpenFile(filepath.Join(binDir, mcVersion, "client.jar"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == metadata.Downloads["client"].Sha1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.Status(fmt.Sprintf("Downloading Minecraft %s Executable\n", mcVersion))
|
|
|
|
resp, err := http.Get(metadata.Downloads["client"].Url)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download executable: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
buff := new(bytes.Buffer)
|
|
|
|
downloaded := 0
|
|
|
|
for {
|
|
|
|
count, err := io.CopyN(buff, resp.Body, BlockSize)
|
|
|
|
if err == io.EOF {
|
|
|
|
downloaded += int(count)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error Downloading executable: %e\n", err)
|
|
|
|
}
|
|
|
|
downloaded += int(count)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download", downloaded, resp.ContentLength)
|
|
|
|
}
|
|
|
|
data := buff.Bytes()
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) != metadata.Downloads["client"].Sha1 {
|
|
|
|
return fmt.Errorf("unable to download executable: Sha1 Mismatch\n")
|
|
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(binDir, mcVersion), 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download executable: Unable to create directory\n")
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(filepath.Join(binDir, mcVersion, "client.jar"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to download executable: Unable to open file\n")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
wruntime.EventsEmit(a.Ctx, "download_complete")
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetBaseLaunchArgs(mcVersion string, libDir string, binDir string, assetDir string, gameDir string) ([]string, error) {
|
|
|
|
args := []string{}
|
|
|
|
metadata, err := GetVersionMetadata(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return args, fmt.Errorf("GetLaunchArgs: %e\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
searchArgs := []string{}
|
|
|
|
|
|
|
|
if metadata.MinecraftArguments != "" {
|
|
|
|
searchArgs = strings.Split(metadata.MinecraftArguments, " ")
|
|
|
|
} else {
|
|
|
|
searchArgs = metadata.Arguments.Game
|
|
|
|
}
|
|
|
|
|
|
|
|
args = append(args, "-Djava.library.path="+filepath.Join(gameDir, "natives"))
|
|
|
|
args = append(args, "-Djna.tmpdir="+filepath.Join(gameDir, "natives"))
|
|
|
|
args = append(args, "-Dorg.lwjgl.system.SharedLibraryExtractpath="+filepath.Join(gameDir, "natives"))
|
|
|
|
args = append(args, "-Dio.netty.native.workdir="+filepath.Join(gameDir, "natives"))
|
2024-10-30 19:11:24 -06:00
|
|
|
loggingArg := strings.ReplaceAll(metadata.Logging.Client.Argument, "${path}", filepath.Join(gameDir, "log4j2.xml"))
|
|
|
|
if loggingArg != "" {
|
|
|
|
args = append(args, loggingArg)
|
|
|
|
}
|
2024-10-30 15:39:11 -06:00
|
|
|
args = append(args, "-Xms512m")
|
|
|
|
args = append(args, "-Xmx1024m")
|
|
|
|
args = append(args, "-cp")
|
|
|
|
arg := ""
|
2024-10-30 19:11:24 -06:00
|
|
|
separater := ":"
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
separater = ";"
|
|
|
|
}
|
2024-10-30 15:39:11 -06:00
|
|
|
for _, lib := range metadata.Libraries {
|
2024-10-30 19:11:24 -06:00
|
|
|
arg += filepath.Join(libDir, lib.Downloads.Artifact.Path) + separater
|
2024-10-30 15:39:11 -06:00
|
|
|
}
|
|
|
|
arg += filepath.Join(binDir, mcVersion, "client.jar")
|
|
|
|
args = append(args, arg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
args = append(args, metadata.MainClass)
|
|
|
|
|
|
|
|
for _, val := range searchArgs {
|
|
|
|
switch val {
|
|
|
|
case "${version_name}":
|
|
|
|
args = append(args, mcVersion)
|
|
|
|
case "${game_directory}":
|
|
|
|
args = append(args, gameDir)
|
|
|
|
case "${assets_root}":
|
|
|
|
args = append(args, assetDir)
|
|
|
|
case "${clientid}":
|
|
|
|
args = append(args, client_id)
|
|
|
|
case "${version_type}":
|
|
|
|
args = append(args, metadata.Type)
|
|
|
|
case "${user_type}":
|
|
|
|
args = append(args, "mojang")
|
|
|
|
case "${assets_index_name}":
|
|
|
|
args = append(args, metadata.Assets)
|
|
|
|
case "${user_properties}":
|
2024-10-30 19:11:24 -06:00
|
|
|
args = append(args, "{}")
|
2024-10-30 15:39:11 -06:00
|
|
|
default:
|
|
|
|
args = append(args, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func GetOfflineLaunchArgs(mcVersion string, libDir string, binDir string, assetDir string, gameDir string, playerName string) ([]string, error) {
|
|
|
|
args, err := GetBaseLaunchArgs(mcVersion, libDir, binDir, assetDir, gameDir)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, fmt.Errorf("GatOfflineLaunchArgs: %e\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for ind, val := range args {
|
|
|
|
switch val{
|
|
|
|
case "${auth_player_name}":
|
|
|
|
args[ind] = playerName
|
|
|
|
case "${auth_uuid}":
|
|
|
|
args[ind] = "null"
|
|
|
|
case "${auth_access_token}":
|
|
|
|
args[ind] = "null"
|
|
|
|
case "${auth_xuid}":
|
|
|
|
args[ind] = "null"
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
2024-10-30 17:16:10 -06:00
|
|
|
func GetOnlineLaunchArgs(mcVersion string, libDir string, binDir string, assetDir string, gameDir string, auth LauncherAuth) ([]string, error) {
|
2024-10-30 15:39:11 -06:00
|
|
|
args, err := GetBaseLaunchArgs(mcVersion, libDir, binDir, assetDir, gameDir)
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, fmt.Errorf("GatOfflineLaunchArgs: %e\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for ind, val := range args {
|
|
|
|
switch val{
|
|
|
|
case "${auth_player_name}":
|
2024-10-30 17:16:10 -06:00
|
|
|
args[ind] = auth.Name
|
2024-10-30 15:39:11 -06:00
|
|
|
case "${auth_uuid}":
|
2024-10-30 17:16:10 -06:00
|
|
|
args[ind] = auth.Id
|
2024-10-30 15:39:11 -06:00
|
|
|
case "${auth_access_token}":
|
2024-10-30 17:16:10 -06:00
|
|
|
args[ind] = auth.Token
|
2024-10-30 15:39:11 -06:00
|
|
|
case "${auth_xuid}":
|
2024-10-30 17:16:10 -06:00
|
|
|
args[ind] = auth.Id
|
2024-10-30 15:39:11 -06:00
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return args, nil
|
|
|
|
}
|