Working on division between modpack and instance

This commit is contained in:
Samuel Walker 2024-10-26 14:00:53 -06:00
parent db9ba30442
commit 4bcc2703d6
21 changed files with 503 additions and 140 deletions

34
fclauncher/2 Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"context"
"os"
"path/filepath"
)
type Instance struct {
InstanceName string
ModpackId string
ModpackVersion string
}
type InstanceManager struct {
instances []Instance
PrismLauncher Prism
ctx context.Context
}
func (i *InstanceManager)SearchInstances() {
i.instances = []Instance{}
dir := i.PrismLauncher.GetInstanceDir()
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()
}
}

View File

@ -0,0 +1,67 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
)
type Instance struct {
InstanceName string
ModpackId string
ModpackVersion string
}
type InstanceManager struct {
instances []Instance
app *App
}
func (i *InstanceManager)SearchInstances() {
i.instances = []Instance{}
dir := i.app.PrismLauncher.GetInstanceDir()
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)
}
}
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)
}
func (i *InstanceManager)GetInstances() []Instance{
return i.instances
}

View File

@ -4,7 +4,6 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"fmt"
"io"
"os"
@ -15,11 +14,11 @@ import (
"github.com/zhyee/zipstream"
)
type Java struct {
ctx context.Context
type JavaManager struct {
app *App
}
func (Java)CheckJavaVer(version int) bool{
func (JavaManager)CheckJavaVer(version int) bool{
suffix := "lin"
if runtime.GOOS == "windows" {
suffix = "win"
@ -35,13 +34,13 @@ func (Java)CheckJavaVer(version int) bool{
}
func (j *Java)InstallJavaVer(version int) {
func (j *JavaManager)InstallJavaVer(version int) {
suffix := "lin.tar.gz"
if runtime.GOOS == "windows" {
suffix = "win.zip"
}
buff := new(bytes.Buffer)
HttpDownload("java/"+fmt.Sprintf("java-%d-%s", version, suffix), buff, j.ctx)
HttpDownload("java/"+fmt.Sprintf("java-%d-%s", version, suffix), buff, j.app.Ctx)
path, _ := os.UserConfigDir()
suffix = "lin"
if runtime.GOOS == "windows" {

62
fclauncher/Modpack.go Normal file
View File

@ -0,0 +1,62 @@
package main
import (
"bytes"
"encoding/json"
"time"
)
type Modpack struct {
Name string
Id string
Last_updated string
Versions []Version
}
type Version struct {
Version string
Data time.Time
File string
}
type ModpackManager struct {
app *App
Modpacks []Modpack
}
func (m *ModpackManager) QuerryModpacks() {
buff := new(bytes.Buffer)
err := HttpDownload("modpacks.json", buff, nil)
if err != nil {
return
}
err = json.Unmarshal(buff.Bytes(), &m.Modpacks)
if err != nil {
return
}
for ind, pack := range m.Modpacks {
buff = new(bytes.Buffer)
err = HttpDownload(pack.Id + "/versions.json", buff, nil)
if err != nil {
continue
}
json.Unmarshal(buff.Bytes(), &pack.Versions)
m.Modpacks[ind] = pack
}
}
func (m *ModpackManager) GetModpacks() []Modpack{
return m.Modpacks
}
func (m *ModpackManager) GetModpack(id string) Modpack {
for _, pack := range m.Modpacks {
if pack.Id == id {
return pack
}
}
return Modpack{}
}

View File

@ -2,36 +2,26 @@ package main
import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/zhyee/zipstream"
)
type Prism struct {
Instances []Instance
ctx context.Context
app *App
}
type Instance struct {
Path string
Name string
MCVer string
JavaVer string
}
type Version struct {
Version string
Data time.Time
File string
}
func (Prism) CheckInstalled() bool {
path, _ := os.UserConfigDir()
@ -45,11 +35,13 @@ func (Prism) CheckInstalled() bool {
func (p *Prism) Install() {
suffix := "lin.tar.gz"
shortSuffix := "lin"
if runtime.GOOS == "windows" {
suffix = "win.zip"
shortSuffix = "win"
}
buff := new(bytes.Buffer)
HttpDownload("prism/prism-"+suffix, buff, p.ctx)
HttpDownload("prism/prism-"+suffix, buff, p.app.Ctx)
path, _ := os.UserConfigDir()
os.MkdirAll(filepath.Join(path, "FCLauncher", "prism"), 0755)
if runtime.GOOS == "windows" {
@ -121,18 +113,51 @@ func (p *Prism) Install() {
}
}
}
func (p *Prism)InstallModpack(modpack Modpack){
buff := new(bytes.Buffer)
HttpDownload(modpack.Id + "/versions.json", buff, nil)
var versions []Version
json.Unmarshal(buff.Bytes(), &versions)
version := versions[len(versions)-1]
dname, _ := os.MkdirTemp("", "fclauncher-*")
f, _ := os.OpenFile(filepath.Join(dname, modpack.Name+".mrpack"), os.O_CREATE|os.O_RDWR, 0755)
dir, _ := os.UserConfigDir()
buff = new(bytes.Buffer)
HttpDownload("prism/prismlauncher.cfg", buff, nil)
scanner := bufio.NewScanner(buff)
f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "prism", "prismlauncher.cfg"), os.O_CREATE|os.O_RDWR, 0755)
defer f.Close()
HttpDownload(modpack.Id + "/" + version.File, f, p.ctx)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "JavaPath") {
line = fmt.Sprintf("JavaPath=%s/FCLauncher/java/java-21-%s", dir, shortSuffix)
}
if strings.HasPrefix(line, "LastHostname") {
host, _ := os.Hostname()
line = fmt.Sprintf("LastHostname=%s", host)
}
f.WriteString(line + "\n")
}
}
func (Prism)GetInstanceDir() string{
dir, _ := os.UserConfigDir()
return filepath.Join(dir, "FCLauncher", "prism", "instances")
}
func (Prism)getExecutableName() string {
execName := "PrismLauncher"
if runtime.GOOS == "windows" {
execName = "prismlauncher.exe"
}
return execName
}
func (p *Prism)ImportModpack(path string) {
dir, _ := os.UserConfigDir()
child := exec.Command(filepath.Join(dir, "FCLauncher", "prism", p.getExecutableName()), "-I", path)
child.Start()
tokens := strings.Split(path, string(os.PathSeparator))
versionPath := filepath.Join(dir, "FCLauncher", "prism", "instances",strings.Split(tokens[len(tokens)-1], ".")[0], ".minecraft", "version.txt")
for {
time.Sleep(time.Second * 3)
if _, err := os.Stat(versionPath); err == nil {
break
}
}
child.Process.Kill()
}

View File

@ -1,91 +1,69 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// App struct
type App struct {
ctx context.Context
prism Prism
java Java
Ctx context.Context
PrismLauncher Prism
Java JavaManager
Instance InstanceManager
Modpacks ModpackManager
}
type Modpack struct {
Name string
Id string
Last_updated string
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
a := &App{}
a.PrismLauncher = Prism{app: a}
a.Java = JavaManager{app: a}
a.Instance = InstanceManager{app: a}
a.Modpacks = ModpackManager{app: a}
return a
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
a.Ctx = ctx
}
// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}
func (a *App) GetModpacks() []Modpack {
buff := new(bytes.Buffer)
err := HttpDownload("modpacks.json", buff, nil)
if err != nil {
fmt.Println(err)
}
body, err := io.ReadAll(buff)
var modpacks []Modpack
err = json.Unmarshal([]byte(body), &modpacks)
if err != nil {
fmt.Println(err.Error())
return nil
}
return modpacks
}
func (a *App) CheckPrerequisites() {
a.status("Checking Prism")
a.prism = Prism{ctx: a.ctx}
res := a.prism.CheckInstalled()
a.Status("Querrying Existing Instances")
a.Instance.SearchInstances()
a.Status("Pulling Modpacks")
a.Modpacks.QuerryModpacks()
a.Status("Checking Prism")
res := a.PrismLauncher.CheckInstalled()
if res {
a.status("Prism OK")
a.Status("Prism OK")
} else {
a.status("Prism MISSING")
a.status("Installing Prism")
a.prism.Install()
a.status("Prism Installed")
a.Status("Prism MISSING")
a.Status("Installing Prism")
a.PrismLauncher.Install()
a.Status("Prism Installed")
}
a.status("Checking Java 21")
a.java = Java{ctx: a.ctx}
res = a.java.CheckJavaVer(21)
a.Status("Checking Java 21")
res = a.Java.CheckJavaVer(21)
if res {
a.status("Java 21 OK")
a.Status("Java 21 OK")
} else {
a.status("Java 21 MISSING")
a.status("Installing Java 21")
a.java.InstallJavaVer(21)
a.status("Java 21 Installed")
a.Status("Java 21 MISSING")
a.Status("Installing Java 21")
a.Java.InstallJavaVer(21)
a.Status("Java 21 Installed")
}
}
func (a *App) InstallModpack(pack Modpack) {
a.status(fmt.Sprintf("Installing %s\n", pack.Name))
a.prism.InstallModpack(pack)
}
func (a *App) status(status string) {
func (a *App) Status(status string) {
fmt.Printf("LOG: %s\n", status)
runtime.EventsEmit(a.ctx, "status", status)
runtime.EventsEmit(a.Ctx, "status", status)
}

View File

@ -1,6 +1,6 @@
<script lang="ts">
import logo from './assets/images/logo-universal.png'
import Client from './Client.svelte'
import Instances from './Instances.svelte'
import Loading from './Loading.svelte'
import {CheckPrerequisites} from '../wailsjs/go/main/App.js'
import { onMount } from 'svelte'
@ -20,7 +20,7 @@
{#if $loading}
<Loading />
{:else}
<Client />
<Instances />
{/if}
</main>

View File

@ -1,32 +0,0 @@
<script lang="ts">
import {GetModpacks, InstallModpack} from '../wailsjs/go/main/App.js'
import {onMount} from 'svelte'
import {loading} from './global.ts'
let modpacks: string[] = []
let pack: Modpack
onMount(() => {
GetModpacks().then((result) => {
modpacks = result
pack = modpacks[0]
})
})
function onclick(event) {
$loading = true
InstallModpack(pack).then(() => {$loading = false})
}
</script>
<main>
<select bind:value={pack} name="pack">Select a Modpack:
{#each modpacks as pack}
<option value={pack}>{pack.Name}</option>
{/each}
</select>
<button on:click={onclick}>Launch</button>
</main>
<style>
</style>

View File

@ -0,0 +1,74 @@
<script lang="ts">
import {GetModpacks, GetModpack} from '../wailsjs/go/main/ModpackManager.js'
import {InstallModpack, GetInstances} from '../wailsjs/go/main/InstanceManager.js'
import {onMount} from 'svelte'
import {loading} from './global.ts'
let modpacks: Modpack[] = []
let pack: string
let instances: Instance[] = []
let instance: Instance
let addingInstance: boolean = false
let name: string = "New Modpack"
onMount(() => {
GetModpacks().then((result) => {
modpacks = result
pack = modpacks[0].Id
name = modpacks[0].Name
})
GetInstances().then((result) => {
instances = result
instance = instances[0]
})
})
function onclick(event) {
}
function install(){
$loading = true
GetModpack(pack).then((result) => {
InstallModpack(result, name).then(() => {
onMount()
addingInstance = false
$loading = false
})
})
}
function onchange(event){
GetModpack(event.target.value).then((result) => {
name = result.Name
})
}
</script>
<main>
<select bind:value={instance} name="pack">Select a Modpack:
{#each instances as instance}
<option value={instance}>{instance.InstanceName}</option>
{/each}
</select>
<button on:click={onclick}>Launch</button>
<br/>
{#if addingInstance}
<select id="pack" on:change={onchange} bind:value={pack} name="pack">Select a Modpack:
{#each modpacks as pack}
<option value={pack.Id}>{pack.Name}</option>
{/each}
</select>
<br/>
<input bind:value={name} />
<br/>
<button on:click={install}>Install</button>
<button on:click={() => {addingInstance = false}}>Cancel</button>
{:else}
<button on:click={() => {addingInstance = true}}>Add Instance</button>
{/if}
</main>
<style>
</style>

View File

@ -1,11 +1,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {main} from '../models';
export function CheckPrerequisites():Promise<void>;
export function GetModpacks():Promise<Array<main.Modpack>>;
export function Greet(arg1:string):Promise<string>;
export function InstallModpack(arg1:main.Modpack):Promise<void>;
export function Status(arg1:string):Promise<void>;

View File

@ -6,14 +6,6 @@ export function CheckPrerequisites() {
return window['go']['main']['App']['CheckPrerequisites']();
}
export function GetModpacks() {
return window['go']['main']['App']['GetModpacks']();
}
export function Greet(arg1) {
return window['go']['main']['App']['Greet'](arg1);
}
export function InstallModpack(arg1) {
return window['go']['main']['App']['InstallModpack'](arg1);
export function Status(arg1) {
return window['go']['main']['App']['Status'](arg1);
}

View File

@ -0,0 +1,9 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {main} from '../models';
export function GetInstances():Promise<Array<main.Instance>>;
export function InstallModpack(arg1:main.Modpack,arg2:string):Promise<void>;
export function SearchInstances():Promise<void>;

View File

@ -0,0 +1,15 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function GetInstances() {
return window['go']['main']['InstanceManager']['GetInstances']();
}
export function InstallModpack(arg1, arg2) {
return window['go']['main']['InstanceManager']['InstallModpack'](arg1, arg2);
}
export function SearchInstances() {
return window['go']['main']['InstanceManager']['SearchInstances']();
}

View File

@ -0,0 +1,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function CheckJavaVer(arg1:number):Promise<boolean>;
export function InstallJavaVer(arg1:number):Promise<void>;

View File

@ -0,0 +1,11 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function CheckJavaVer(arg1) {
return window['go']['main']['JavaManager']['CheckJavaVer'](arg1);
}
export function InstallJavaVer(arg1) {
return window['go']['main']['JavaManager']['InstallJavaVer'](arg1);
}

View File

@ -0,0 +1,9 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {main} from '../models';
export function GetModpack(arg1:string):Promise<main.Modpack>;
export function GetModpacks():Promise<Array<main.Modpack>>;
export function QuerryModpacks():Promise<void>;

View File

@ -0,0 +1,15 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function GetModpack(arg1) {
return window['go']['main']['ModpackManager']['GetModpack'](arg1);
}
export function GetModpacks() {
return window['go']['main']['ModpackManager']['GetModpacks']();
}
export function QuerryModpacks() {
return window['go']['main']['ModpackManager']['QuerryModpacks']();
}

View File

@ -0,0 +1,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function CheckInstalled():Promise<boolean>;
export function GetInstanceDir():Promise<string>;
export function ImportModpack(arg1:string):Promise<void>;
export function Install():Promise<void>;

View File

@ -0,0 +1,19 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function CheckInstalled() {
return window['go']['main']['Prism']['CheckInstalled']();
}
export function GetInstanceDir() {
return window['go']['main']['Prism']['GetInstanceDir']();
}
export function ImportModpack(arg1) {
return window['go']['main']['Prism']['ImportModpack'](arg1);
}
export function Install() {
return window['go']['main']['Prism']['Install']();
}

View File

@ -1,9 +1,61 @@
export namespace main {
export class Instance {
InstanceName: string;
ModpackId: string;
ModpackVersion: string;
static createFrom(source: any = {}) {
return new Instance(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.InstanceName = source["InstanceName"];
this.ModpackId = source["ModpackId"];
this.ModpackVersion = source["ModpackVersion"];
}
}
export class Version {
Version: string;
// Go type: time
Data: any;
File: string;
static createFrom(source: any = {}) {
return new Version(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.Version = source["Version"];
this.Data = this.convertValues(source["Data"], null);
this.File = source["File"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class Modpack {
Name: string;
Id: string;
Last_updated: string;
Versions: Version[];
static createFrom(source: any = {}) {
return new Modpack(source);
@ -14,7 +66,26 @@ export namespace main {
this.Name = source["Name"];
this.Id = source["Id"];
this.Last_updated = source["Last_updated"];
this.Versions = this.convertValues(source["Versions"], Version);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}

View File

@ -27,6 +27,10 @@ func main() {
OnStartup: app.startup,
Bind: []interface{}{
app,
&app.Instance,
&app.PrismLauncher,
&app.Java,
&app.Modpacks,
},
})