2024-10-26 14:00:53 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-10-26 18:14:12 -06:00
|
|
|
"bufio"
|
2024-10-26 14:00:53 -06:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
2024-10-30 15:39:11 -06:00
|
|
|
"os/exec"
|
2024-10-26 14:00:53 -06:00
|
|
|
"path/filepath"
|
2024-10-26 18:14:12 -06:00
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2024-10-30 19:11:24 -06:00
|
|
|
|
|
|
|
wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
2024-10-26 14:00:53 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type Instance struct {
|
|
|
|
InstanceName string
|
|
|
|
ModpackId string
|
|
|
|
ModpackVersion string
|
2024-10-30 15:39:11 -06:00
|
|
|
MinecraftVersion string
|
|
|
|
ForgeVersion string
|
|
|
|
NeoForgeVersion string
|
|
|
|
FabricVersion string
|
|
|
|
QuiltVersion string
|
|
|
|
JavaVersion int
|
2024-10-31 16:23:38 -06:00
|
|
|
Libraries []string
|
|
|
|
MainClass string
|
2024-10-26 14:00:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type InstanceManager struct {
|
|
|
|
instances []Instance
|
|
|
|
app *App
|
|
|
|
}
|
|
|
|
|
2024-10-26 18:14:12 -06:00
|
|
|
type mmcpack struct {
|
|
|
|
Components []component
|
|
|
|
}
|
|
|
|
|
|
|
|
type component struct {
|
|
|
|
Uid string
|
|
|
|
Version string
|
|
|
|
}
|
|
|
|
|
2024-10-26 14:00:53 -06:00
|
|
|
func (i *InstanceManager)SearchInstances() {
|
|
|
|
i.instances = []Instance{}
|
2024-10-30 15:39:11 -06:00
|
|
|
dir, _ := os.UserConfigDir()
|
|
|
|
dir = filepath.Join(dir, "FCLauncher", "instances")
|
2024-10-26 14:00:53 -06:00
|
|
|
if _, err := os.Stat(dir); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
subdirs, _ := os.ReadDir(dir)
|
|
|
|
for _, d := range subdirs {
|
|
|
|
if !d.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _, err := os.Stat(filepath.Join(dir, d.Name(), "instance.json")); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
f, _ := os.OpenFile(filepath.Join(dir, d.Name(), "instance.json"), os.O_RDONLY, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
buff := new(bytes.Buffer)
|
|
|
|
io.Copy(buff, f)
|
|
|
|
instance := Instance{}
|
|
|
|
json.Unmarshal(buff.Bytes(), &instance)
|
|
|
|
fmt.Printf("Found Instance: %+v\n", instance)
|
|
|
|
i.instances = append(i.instances, instance)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-26 18:14:12 -06:00
|
|
|
func (i *InstanceManager)checkJavaVersion(instance Instance){
|
|
|
|
infoPath := filepath.Join(i.app.PrismLauncher.GetInstanceDir(), instance.InstanceName, "mmc-pack.json")
|
|
|
|
f, _ := os.OpenFile(infoPath, os.O_RDONLY, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
dataStr,_ := io.ReadAll(f)
|
|
|
|
var data mmcpack
|
|
|
|
json.Unmarshal(dataStr, &data)
|
|
|
|
mc_version := "0.0"
|
|
|
|
for _, comp := range data.Components {
|
|
|
|
if comp.Uid == "net.minecraft" {
|
|
|
|
mc_version = comp.Version
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("MC Version: %s",mc_version)
|
|
|
|
tokensStr := strings.Split(mc_version, ".")
|
|
|
|
tokens := []int{0, 0, 0}
|
|
|
|
tokens[0], _ = strconv.Atoi(tokensStr[0])
|
|
|
|
tokens[1], _ = strconv.Atoi(tokensStr[1])
|
2024-10-26 19:27:39 -06:00
|
|
|
if len(tokensStr) > 2 {
|
|
|
|
tokens[2], _ = strconv.Atoi(tokensStr[2])
|
|
|
|
}
|
2024-10-26 18:14:12 -06:00
|
|
|
javaVer := 8
|
|
|
|
if tokens[1] == 17 {
|
|
|
|
javaVer = 17
|
|
|
|
} else if tokens[1] == 18 || tokens[1] == 19 {
|
|
|
|
javaVer = 17
|
|
|
|
} else if tokens[1] > 19 {
|
|
|
|
if tokens[1] == 20 && tokens[2] < 5 {
|
|
|
|
javaVer = 17
|
|
|
|
} else {
|
|
|
|
javaVer = 21
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("Req Java Version: %d",javaVer)
|
|
|
|
if !i.app.Java.CheckJavaVer(javaVer) {
|
|
|
|
i.app.Java.InstallJavaVer(javaVer)
|
|
|
|
}
|
|
|
|
|
|
|
|
confPath := filepath.Join(i.app.PrismLauncher.GetInstanceDir(), instance.InstanceName, "instance.cfg")
|
|
|
|
f, _ = os.OpenFile(confPath, os.O_RDONLY, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
buff := new(bytes.Buffer)
|
|
|
|
io.Copy(buff, f)
|
|
|
|
sc := bufio.NewScanner(buff)
|
|
|
|
f, _ = os.OpenFile(confPath, os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
plat := "lin"
|
|
|
|
exe := "java"
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
plat = "win"
|
|
|
|
exe = "Java.exe"
|
|
|
|
}
|
|
|
|
confDir, _ := os.UserConfigDir()
|
|
|
|
found := false
|
|
|
|
for sc.Scan() {
|
|
|
|
line := sc.Text()
|
|
|
|
if strings.HasPrefix(line, "JavaPath=") {
|
2024-10-26 22:33:33 -06:00
|
|
|
line = fmt.Sprintf("JavaPath=%s/FCLauncher/java/java-%d-%s/bin/%s", strings.ReplaceAll(confDir, "\\", "/"), javaVer, plat, exe)
|
2024-10-26 18:14:12 -06:00
|
|
|
found = true
|
|
|
|
}
|
|
|
|
f.WriteString(line+"\n")
|
|
|
|
}
|
|
|
|
if !found {
|
2024-10-26 22:33:33 -06:00
|
|
|
line := fmt.Sprintf("JavaPath=%s/FCLauncher/java/java-%d-%s/bin/%s", strings.ReplaceAll(confDir, "\\", "/"), javaVer, plat, exe)
|
2024-10-26 18:14:12 -06:00
|
|
|
f.WriteString(line+"\n")
|
|
|
|
f.WriteString("OverrideJavaLocation=true\nOverrideJava=true\n")
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
}
|
|
|
|
|
2024-10-26 14:00:53 -06:00
|
|
|
func (i *InstanceManager)InstallModpack(modpack Modpack, instanceName string){
|
|
|
|
i.app.Status(fmt.Sprintf("Installing %s", modpack.Name))
|
|
|
|
version := modpack.Versions[len(modpack.Versions)-1]
|
|
|
|
dname, _ := os.MkdirTemp("", "fclauncher-*")
|
|
|
|
f, _ := os.OpenFile(filepath.Join(dname, instanceName+".mrpack"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
HttpDownload(modpack.Id + "/" + version.File, f, i.app.Ctx)
|
|
|
|
i.app.PrismLauncher.ImportModpack(f.Name())
|
|
|
|
instance := Instance{InstanceName: instanceName, ModpackVersion: version.Version, ModpackId: modpack.Id}
|
|
|
|
i.instances = append(i.instances, instance)
|
|
|
|
f, _ = os.OpenFile(filepath.Join(i.app.PrismLauncher.GetInstanceDir(), instanceName, "instance.json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := json.Marshal(instance)
|
|
|
|
f.Write(data)
|
2024-10-26 18:14:12 -06:00
|
|
|
i.checkJavaVersion(instance)
|
|
|
|
|
2024-10-26 14:00:53 -06:00
|
|
|
}
|
|
|
|
|
2024-10-29 21:48:59 -06:00
|
|
|
func (i *InstanceManager)InstallVanilla(version string, instanceName string) {
|
2024-10-30 15:39:11 -06:00
|
|
|
dir, _ := os.UserConfigDir()
|
|
|
|
err := DownloadAssets(version, filepath.Join(dir, "FCLauncher", "assets"), *i.app)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Unable to download assets: %s\n", err)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Assets Downloaded")
|
|
|
|
}
|
|
|
|
err = DownloadLibraries(version, filepath.Join(dir, "FCLauncher", "lib"), *i.app)
|
2024-10-29 21:48:59 -06:00
|
|
|
if err != nil {
|
2024-10-30 15:39:11 -06:00
|
|
|
fmt.Printf("Unable to download libs: %s\n", err)
|
2024-10-29 21:48:59 -06:00
|
|
|
} else {
|
2024-10-30 15:39:11 -06:00
|
|
|
fmt.Printf("Libs Downloaded")
|
|
|
|
}
|
|
|
|
InstallNatives(version, filepath.Join(dir, "FCLauncher", "instances", instanceName, "minecraft", "natives"))
|
2024-10-30 15:58:05 -06:00
|
|
|
DownloadLoggingConfig(version, filepath.Join(dir, "FCLauncher", "instances", instanceName, "minecraft"))
|
2024-10-30 15:39:11 -06:00
|
|
|
err = DownloadExecutable(version, filepath.Join(dir, "FCLauncher", "bin"), *i.app)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Unable to download binaries: %s\n", err)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Binaries Downloaded")
|
|
|
|
}
|
|
|
|
metadata, err := GetVersionMetadata(version)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to pull metadata: %s\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = os.MkdirAll(filepath.Join(dir, "FCLauncher", "instances", instanceName, "minecraft"), 0755)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to create directory: %s\n", err)
|
|
|
|
}
|
2024-10-31 16:23:38 -06:00
|
|
|
instance := Instance{InstanceName: instanceName, MinecraftVersion: version, JavaVersion: metadata.JavaVersion.MajorVersion, MainClass: metadata.MainClass}
|
|
|
|
for _, lib := range metadata.Libraries {
|
|
|
|
instance.Libraries = append(instance.Libraries, lib.Downloads.Artifact.Path)
|
|
|
|
}
|
2024-10-30 15:39:11 -06:00
|
|
|
data, err := json.Marshal(instance)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to marshal json data: %s\n", err)
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile(filepath.Join(dir, "FCLauncher", "instances", instanceName, "instance.json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to open file: %s\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
_, err = f.Write(data)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to write data: %s\n", err)
|
|
|
|
}
|
|
|
|
i.instances = append(i.instances, instance)
|
|
|
|
|
|
|
|
if !i.app.Java.CheckJavaVer(instance.JavaVersion) {
|
|
|
|
i.app.Status(fmt.Sprintf("Installing Java Version %d", instance.JavaVersion))
|
|
|
|
i.app.Java.InstallJavaVer(instance.JavaVersion)
|
2024-10-29 21:48:59 -06:00
|
|
|
}
|
2024-10-30 15:39:11 -06:00
|
|
|
|
2024-10-29 21:48:59 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-26 14:00:53 -06:00
|
|
|
func (i *InstanceManager)GetInstances() []Instance{
|
|
|
|
return i.instances
|
|
|
|
}
|
|
|
|
|
2024-10-26 18:14:12 -06:00
|
|
|
func (i *InstanceManager)CheckUpdate(instance Instance){
|
2024-10-30 15:39:11 -06:00
|
|
|
return
|
2024-10-26 18:14:12 -06:00
|
|
|
i.app.Status("Checking for Updates")
|
|
|
|
i.app.Modpacks.QuerryModpacks()
|
|
|
|
pack := i.app.Modpacks.GetModpack(instance.ModpackId)
|
|
|
|
if pack.Versions[len(pack.Versions)-1].Version == instance.ModpackVersion {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
i.app.Status(fmt.Sprintf("Updating %s", instance.InstanceName))
|
|
|
|
version := pack.Versions[len(pack.Versions)-1]
|
|
|
|
dname, _ := os.MkdirTemp("", "fclauncher-*")
|
|
|
|
f, _ := os.OpenFile(filepath.Join(dname, instance.InstanceName+".mrpack"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
HttpDownload(pack.Id + "/" + version.File, f, i.app.Ctx)
|
|
|
|
i.app.PrismLauncher.ImportModpack(f.Name())
|
|
|
|
instance.ModpackVersion = version.Version
|
|
|
|
f, _ = os.OpenFile(filepath.Join(i.app.PrismLauncher.GetInstanceDir(), instance.InstanceName, "instance.json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := json.Marshal(instance)
|
|
|
|
f.Write(data)
|
|
|
|
i.checkJavaVersion(instance)
|
|
|
|
i.SearchInstances()
|
|
|
|
}
|
|
|
|
|
2024-10-31 16:23:38 -06:00
|
|
|
func (i *InstanceManager)GetInstance(instance string) (Instance, error) {
|
2024-10-30 15:39:11 -06:00
|
|
|
instanceObject := Instance{}
|
|
|
|
found := false
|
|
|
|
for _, inst := range i.instances {
|
|
|
|
if inst.InstanceName == instance {
|
|
|
|
instanceObject = inst
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2024-10-31 16:23:38 -06:00
|
|
|
return Instance{}, fmt.Errorf("unable to find instance %s\n", instance)
|
|
|
|
}
|
|
|
|
return instanceObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstanceManager)LaunchInstance(instance string) {
|
|
|
|
dir, err := os.UserConfigDir()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to get config directory\n")
|
|
|
|
}
|
|
|
|
instanceObject, err := i.GetInstance(instance)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Unable to find instance\n")
|
2024-10-30 15:39:11 -06:00
|
|
|
}
|
|
|
|
execName := "java"
|
|
|
|
suffix := "lin"
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
execName = "Java.exe"
|
|
|
|
suffix = "win"
|
|
|
|
}
|
|
|
|
dir = filepath.Join(dir, "FCLauncher")
|
2024-10-30 17:16:10 -06:00
|
|
|
auth, err := MicrosoftAuth(i.app.Auth)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to authenticate: %s\n", err)
|
|
|
|
return
|
|
|
|
}
|
2024-10-31 16:23:38 -06:00
|
|
|
args, err := GetOnlineLaunchArgs(instanceObject.MinecraftVersion, instanceObject, filepath.Join(dir, "lib"), filepath.Join(dir, "bin"), filepath.Join(dir, "assets"), filepath.Join(dir, "instances", instance, "minecraft"), auth)
|
2024-10-30 15:39:11 -06:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to get launch args: %s\n", err)
|
|
|
|
}
|
|
|
|
fmt.Printf("Args: %+v", args)
|
|
|
|
child := exec.Command(filepath.Join(dir, "java", fmt.Sprintf("java-%d-%s", instanceObject.JavaVersion, suffix), "bin", execName), args...)
|
2024-10-30 19:11:24 -06:00
|
|
|
wruntime.WindowHide(i.app.Ctx)
|
2024-10-30 15:39:11 -06:00
|
|
|
data, err := child.CombinedOutput()
|
2024-10-30 19:11:24 -06:00
|
|
|
wruntime.WindowShow(i.app.Ctx)
|
2024-10-30 15:39:11 -06:00
|
|
|
fmt.Printf("Command Output: %s\n", data)
|
|
|
|
}
|
|
|
|
|
2024-10-31 16:23:38 -06:00
|
|
|
|
|
|
|
|
|
|
|
func (i *InstanceManager)InstallFabric(instance string, fabricVersion string) {
|
|
|
|
i.app.Status("Installing Fabric")
|
|
|
|
instanceObject, err := i.GetInstance(instance)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Instance does not exist\n")
|
|
|
|
}
|
|
|
|
metadata, err := GetFabricMetadata(instanceObject.MinecraftVersion, fabricVersion)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("unable to get version metadata\n")
|
|
|
|
}
|
2024-10-31 17:47:50 -06:00
|
|
|
client:
|
2024-10-31 16:23:38 -06:00
|
|
|
for _, lib := range metadata.LauncherMeta.Libraries.Client {
|
2024-10-31 17:47:50 -06:00
|
|
|
tokens := strings.Split(ProcessMavenPath(lib.Name), string(os.PathSeparator))
|
|
|
|
pkg := tokens[len(tokens)-2]
|
2024-10-31 16:46:12 -06:00
|
|
|
instanceObject.Libraries = append(instanceObject.Libraries, filepath.Join(ProcessMavenPath(lib.Name), ProcessMavenFilename(lib.Name)))
|
2024-10-31 17:47:50 -06:00
|
|
|
for ind, path := range instanceObject.Libraries {
|
|
|
|
tokens := strings.Split(path, string(os.PathSeparator))
|
|
|
|
if pkg == tokens[len(tokens)-3] {
|
|
|
|
instanceObject.Libraries[ind] = filepath.Join(ProcessMavenPath(lib.Name), ProcessMavenFilename(lib.Name))
|
|
|
|
fmt.Printf("duplicate library %s\n", pkg)
|
|
|
|
continue client
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 16:23:38 -06:00
|
|
|
}
|
2024-10-31 17:47:50 -06:00
|
|
|
common:
|
2024-10-31 16:23:38 -06:00
|
|
|
for _, lib := range metadata.LauncherMeta.Libraries.Common {
|
2024-10-31 17:47:50 -06:00
|
|
|
tokens := strings.Split(ProcessMavenPath(lib.Name), string(os.PathSeparator))
|
|
|
|
pkg := tokens[len(tokens)-2]
|
2024-10-31 16:46:12 -06:00
|
|
|
instanceObject.Libraries = append(instanceObject.Libraries, filepath.Join(ProcessMavenPath(lib.Name), ProcessMavenFilename(lib.Name)))
|
2024-10-31 17:47:50 -06:00
|
|
|
for ind, path := range instanceObject.Libraries {
|
|
|
|
tokens := strings.Split(path, string(os.PathSeparator))
|
|
|
|
if pkg == tokens[len(tokens)-3] {
|
|
|
|
instanceObject.Libraries[ind] = filepath.Join(ProcessMavenPath(lib.Name), ProcessMavenFilename(lib.Name))
|
|
|
|
fmt.Printf("duplicate library %s\n", pkg)
|
|
|
|
continue common
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 16:23:38 -06:00
|
|
|
}
|
2024-10-31 16:46:12 -06:00
|
|
|
instanceObject.Libraries = append(instanceObject.Libraries, filepath.Join(ProcessMavenPath(metadata.Loader.Maven), ProcessMavenFilename(metadata.Loader.Maven)))
|
2024-10-31 16:23:38 -06:00
|
|
|
|
2024-10-31 16:46:12 -06:00
|
|
|
instanceObject.Libraries = append(instanceObject.Libraries, filepath.Join(ProcessMavenPath(metadata.Intermediary.Maven), ProcessMavenFilename(metadata.Intermediary.Maven)))
|
2024-10-31 16:23:38 -06:00
|
|
|
|
|
|
|
instanceObject.MainClass = metadata.LauncherMeta.MainClass["client"]
|
|
|
|
instanceObject.FabricVersion = fabricVersion
|
|
|
|
dir, _ := os.UserConfigDir()
|
|
|
|
InstallFabricLibs(instanceObject.MinecraftVersion, fabricVersion, filepath.Join(dir, "FCLauncher", "lib"))
|
|
|
|
f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "instances", instance, "instance.json"), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
data, _ := json.Marshal(instanceObject)
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(data)
|
|
|
|
for ind, inst := range i.instances {
|
|
|
|
if inst.InstanceName == instance {
|
|
|
|
i.instances[ind] = instanceObject
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|