Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
542670eaed | |||
09a44fb454 | |||
cd555f0876 | |||
73f3561d3f | |||
32e95a362e | |||
97bf9cf32f | |||
8662153706 | |||
e23db23eae | |||
c52bebedf7 |
2
Makefile
2
Makefile
@ -8,7 +8,7 @@ endif
|
|||||||
DESTDIR:=
|
DESTDIR:=
|
||||||
PREFIX:=/usr/local
|
PREFIX:=/usr/local
|
||||||
|
|
||||||
VERSION=0.1.3
|
VERSION=0.1.5
|
||||||
|
|
||||||
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
|
FLAGS:=-buildmode=pie -modcacherw -mod=readonly -trimpath
|
||||||
LDFLAGS:= -ldflags "-s -w -X main.programVersion=${VERSION}"
|
LDFLAGS:= -ldflags "-s -w -X main.programVersion=${VERSION}"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
|
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
|
||||||
pkgname=mccl
|
pkgname=mccl
|
||||||
pkgver=0.1.3
|
pkgver=0.1.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Console Minecraft launcher"
|
pkgdesc="Console Minecraft launcher"
|
||||||
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
|
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
|
||||||
|
@ -82,8 +82,8 @@ func (ic *InstallCommand) install_vanilla_client() error {
|
|||||||
}
|
}
|
||||||
fmt.Printf("Version %s (%s) was chosen.\n", version.Id, version.Type)
|
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")
|
manifestPath := path.Join(ic.DestDir, "versions", version.Id, version.Id+".json")
|
||||||
|
fmt.Print("Retrieving ", path.Join("versions", version.Id, version.Id+".json"), "... ")
|
||||||
data, err = util.LoadOrDownloadFile(manifestPath, version.Url, version.Sha1, "sha1", -1)
|
data, err = util.LoadOrDownloadFile(manifestPath, version.Url, version.Sha1, "sha1", -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -93,21 +93,19 @@ func (ic *InstallCommand) install_vanilla_client() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Done")
|
|
||||||
|
|
||||||
if !util.IsFileExist(manifestPath) {
|
if !util.IsFileExist(manifestPath) {
|
||||||
fmt.Print("Writing a manifest file to ", manifestPath, " ... ")
|
|
||||||
if err := util.WriteFile(manifestPath, data); err != nil {
|
if err := util.WriteFile(manifestPath, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Done")
|
fmt.Println("Done")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(manifestPath, "does exist. Skipping")
|
fmt.Println("does exist. Skipping")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientPath := path.Join(ic.DestDir, "versions", manifst.Id, manifst.Id+".jar")
|
clientPath := path.Join(ic.DestDir, "versions", manifst.Id, manifst.Id+".jar")
|
||||||
|
fmt.Print("Retrieving ", path.Join("versions", manifst.Id, manifst.Id+".jar"), "... ")
|
||||||
if !util.IsFileExist(clientPath) {
|
if !util.IsFileExist(clientPath) {
|
||||||
fmt.Print("Retrieving and writing a client to ", clientPath, " ... ")
|
|
||||||
data, err = util.LoadOrDownloadFile(clientPath, manifst.Downloads["client"].Url,
|
data, err = util.LoadOrDownloadFile(clientPath, manifst.Downloads["client"].Url,
|
||||||
manifst.Downloads["client"].Sha1, "sha1", manifst.Downloads["client"].Size)
|
manifst.Downloads["client"].Sha1, "sha1", manifst.Downloads["client"].Size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,12 +116,11 @@ func (ic *InstallCommand) install_vanilla_client() error {
|
|||||||
}
|
}
|
||||||
fmt.Println("Done")
|
fmt.Println("Done")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(clientPath, "does exist. Skipping")
|
fmt.Println("does exist. Skipping")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Retrieving an asset index %s.json... ", manifst.AssetIndex.Id)
|
fmt.Print("Retrieving ", path.Join("assets", "indexes", manifst.AssetIndex.Id+".json"), "... ")
|
||||||
assetIndexPath := path.Join(ic.DestDir, "assets", "indexes", manifst.AssetIndex.Id+".json")
|
assetIndexPath := path.Join(ic.DestDir, "assets", "indexes", manifst.AssetIndex.Id+".json")
|
||||||
|
|
||||||
data, err = util.LoadOrDownloadFile(assetIndexPath, manifst.AssetIndex.Url,
|
data, err = util.LoadOrDownloadFile(assetIndexPath, manifst.AssetIndex.Url,
|
||||||
manifst.AssetIndex.Sha1, "sha1", manifst.AssetIndex.Size)
|
manifst.AssetIndex.Sha1, "sha1", manifst.AssetIndex.Size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -134,16 +131,14 @@ func (ic *InstallCommand) install_vanilla_client() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Done")
|
|
||||||
|
|
||||||
if !util.IsFileExist(assetIndexPath) {
|
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 {
|
if err := util.WriteFile(assetIndexPath, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Done")
|
fmt.Println("Done")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s does exist. Skip writing\n", assetIndexPath)
|
fmt.Print("does exist. Skip writing")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print("Retrieving assets...")
|
fmt.Print("Retrieving assets...")
|
||||||
@ -182,8 +177,8 @@ func (ic *InstallCommand) install_vanilla_client() error {
|
|||||||
|
|
||||||
if manifst.Logging.Client.Argument != "" {
|
if manifst.Logging.Client.Argument != "" {
|
||||||
logConfPath := path.Join(ic.DestDir, "assets", "log_configs", manifst.Logging.Client.File.Id)
|
logConfPath := path.Join(ic.DestDir, "assets", "log_configs", manifst.Logging.Client.File.Id)
|
||||||
|
fmt.Print("Retrieving ", path.Join("assets", "log_configs", manifst.Logging.Client.File.Id), "... ")
|
||||||
if !util.IsFileExist(logConfPath) {
|
if !util.IsFileExist(logConfPath) {
|
||||||
fmt.Printf("Retrieving and writing %s...", logConfPath)
|
|
||||||
data, err = util.LoadOrDownloadFile(logConfPath, manifst.Logging.Client.File.Url,
|
data, err = util.LoadOrDownloadFile(logConfPath, manifst.Logging.Client.File.Url,
|
||||||
manifst.Logging.Client.File.Sha1, "sha1", manifst.Logging.Client.File.Size)
|
manifst.Logging.Client.File.Sha1, "sha1", manifst.Logging.Client.File.Size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,7 +23,6 @@ func (lc *ListCommand) Run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("There are following versions installed:")
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
fmt.Println(entry.Name())
|
fmt.Println(entry.Name())
|
||||||
|
@ -36,23 +36,21 @@ func (rc *RunCommand) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rc.UseProfile {
|
if util.IsFileExist(path.Join(rc.GameDir, mcclprofile.ProfileFileName)) {
|
||||||
if util.IsFileExist(path.Join(rc.GameDir, mcclprofile.ProfileFileName)) {
|
p, err := mcclprofile.Load(rc.GameDir)
|
||||||
p, err := mcclprofile.Load(rc.GameDir)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
rc.Username = p.Username
|
||||||
rc.Username = p.Username
|
rc.AuthUuid = p.Uuid
|
||||||
rc.AuthUuid = p.Uuid
|
rc.JavaXmx = p.JavaXmx
|
||||||
rc.JavaXmx = p.JavaXmx
|
} else {
|
||||||
} else {
|
p := mcclprofile.Profile{
|
||||||
p := mcclprofile.Profile{
|
Username: rc.Username,
|
||||||
Username: rc.Username,
|
Uuid: rc.AuthUuid,
|
||||||
Uuid: rc.AuthUuid,
|
JavaXmx: rc.JavaXmx}
|
||||||
JavaXmx: rc.JavaXmx}
|
if err := p.Store(rc.GameDir); err != nil {
|
||||||
if err := p.Store(rc.GameDir); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +103,13 @@ func (rc *RunCommand) run_client() error {
|
|||||||
p["auth_xuid"] = "null"
|
p["auth_xuid"] = "null"
|
||||||
p["user_type"] = "legacy"
|
p["user_type"] = "legacy"
|
||||||
p["version_type"] = manifst.Type
|
p["version_type"] = manifst.Type
|
||||||
p["natives_directory"] = path.Join("", "versions", manifst.Id, "natives")
|
|
||||||
|
nativesId := manifst.Id
|
||||||
|
if manifst.InheritsFrom != "" {
|
||||||
|
nativesId = manifst.InheritsFrom
|
||||||
|
}
|
||||||
|
p["natives_directory"] = path.Join("", "versions", nativesId, "natives")
|
||||||
|
|
||||||
p["launcher_name"] = "mccl"
|
p["launcher_name"] = "mccl"
|
||||||
p["launcher_version"] = "0.1.0"
|
p["launcher_version"] = "0.1.0"
|
||||||
p["classpath"] = manifst.BuildClasspath("", "versions")
|
p["classpath"] = manifst.BuildClasspath("", "versions")
|
||||||
@ -157,8 +161,8 @@ func (rc *RunCommand) run_client() error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fmt.Printf("Minecraft version %s is started with username %s and player's UUID %s.\n",
|
fmt.Printf("Minecraft version %s is started with %s, username %s and player's UUID %s.\n",
|
||||||
manifst.Id, rc.Username, rc.AuthUuid)
|
manifst.Id, rc.JavaXmx, rc.Username, rc.AuthUuid)
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil && (err.Error() != "exec: already started") {
|
if err := cmd.Run(); err != nil && (err.Error() != "exec: already started") {
|
||||||
return err
|
return err
|
||||||
|
167
cmd/mccl/commands/uninstall_command.go
Normal file
167
cmd/mccl/commands/uninstall_command.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"mccl/internal/assets"
|
||||||
|
"mccl/internal/manifest"
|
||||||
|
"mccl/pkg/util"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UninstallCommand struct {
|
||||||
|
Id string
|
||||||
|
GameDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUninstallCommand(id, gameDir string) *UninstallCommand {
|
||||||
|
return &UninstallCommand{Id: id, GameDir: gameDir}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *UninstallCommand) Run() error {
|
||||||
|
if uc.Id == "" {
|
||||||
|
return errors.New("an empty Minecraft version was provided")
|
||||||
|
}
|
||||||
|
if uc.GameDir == "" {
|
||||||
|
return errors.New("an empty path was provided")
|
||||||
|
}
|
||||||
|
return uc.uninstall_client()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *UninstallCommand) uninstall_client() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if uc.GameDir == "." {
|
||||||
|
execPath, _ := os.Executable()
|
||||||
|
uc.GameDir = path.Dir(execPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(uc.GameDir); err != nil {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
manifests := make(map[string]*manifest.Manifest)
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(path.Join(uc.GameDir, "versions"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
data, err := util.ReadFile(path.Join(uc.GameDir, "versions", entry.Name(), entry.Name()+".json"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
manifests[entry.Name()], err = manifest.New(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, manifest := range manifests {
|
||||||
|
if id == uc.Id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest.InheritsFrom == uc.Id {
|
||||||
|
return fmt.Errorf("%s inherits %s. Uninstall %s first", id, uc.Id, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assetIndexInUse := manifests[uc.Id].InheritsFrom != ""
|
||||||
|
logConfigInUse := manifests[uc.Id].InheritsFrom != ""
|
||||||
|
libsToDelete := make(map[string]string)
|
||||||
|
|
||||||
|
for _, lib := range manifests[uc.Id].Libraries {
|
||||||
|
libsToDelete[lib.Name] = lib.Path()
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, manifest := range manifests {
|
||||||
|
if id == uc.Id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assetIndexInUse && manifests[uc.Id].Assets == manifest.Assets {
|
||||||
|
assetIndexInUse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !logConfigInUse && manifests[uc.Id].Logging.Client.File.Id == manifest.Logging.Client.File.Id {
|
||||||
|
logConfigInUse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lib := range manifest.Libraries {
|
||||||
|
if name, ok := libsToDelete[lib.Name]; ok {
|
||||||
|
delete(libsToDelete, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !logConfigInUse {
|
||||||
|
dir := path.Join(uc.GameDir, "assets", "log_configs")
|
||||||
|
if err := os.Remove(path.Join(dir, manifests[uc.Id].Logging.Client.File.Id)); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove assets/log_configs/%s: %s", manifests[uc.Id].Logging.Client.File.Id, err)
|
||||||
|
}
|
||||||
|
if d, err := os.ReadDir(dir); err == nil && len(d) == 0 {
|
||||||
|
if err := os.Remove(path.Join(dir, manifests[uc.Id].Logging.Client.File.Id)); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove assets/log_configs directory: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assetIndexInUse {
|
||||||
|
dir := path.Join(uc.GameDir, "assets")
|
||||||
|
|
||||||
|
assetFiles := make(map[string]*assets.Assets)
|
||||||
|
|
||||||
|
for _, manifest := range manifests {
|
||||||
|
if _, ok := assetFiles[manifest.Assets]; !ok {
|
||||||
|
data, err := os.ReadFile(path.Join(dir, "indexes", manifest.Assets+".json"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read an index %s file: %s", manifest.Assets, err)
|
||||||
|
}
|
||||||
|
assetFiles[manifest.Assets], err = assets.New(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load asset index %s: %s", manifest.Assets, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove := []string{}
|
||||||
|
|
||||||
|
for name, obj := range assetFiles[manifests[uc.Id].Assets].Objects {
|
||||||
|
inUse := false
|
||||||
|
for id, idx := range assetFiles {
|
||||||
|
if id == manifests[uc.Id].Assets {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iobj, ok := idx.Objects[name]; ok && iobj.Hash == obj.Hash {
|
||||||
|
inUse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !inUse {
|
||||||
|
toRemove = append(toRemove, obj.Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hash := range toRemove {
|
||||||
|
if err := os.Remove(path.Join(dir, "objects", hash[:2], hash)); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove an object %s/%s: %s", hash[:2], hash, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(path.Join(dir, "indexes", manifests[uc.Id].Assets+".json")); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove %s: %s", path.Join("assets", "indexes", manifests[uc.Id].Assets+".json"), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range libsToDelete {
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove %s: %s", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"mccl/cmd/mccl/commands"
|
"mccl/cmd/mccl/commands"
|
||||||
mcclprofile "mccl/internal/mccl_profile"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func version() {
|
func version() {
|
||||||
fmt.Fprintf(os.Stderr, "mccl ver. %s\nCopyright (c) 2023 Alexander \"Arav\" Andreev <me@arav.su>\n", programVersion)
|
fmt.Fprintf(os.Stderr, "mccl ver. %s\nCopyright (c) 2023,2024 Alexander \"Arav\" Andreev <me@arav.su>\n", programVersion)
|
||||||
fmt.Fprintln(os.Stderr, "URL: https://git.arav.su/Arav/mccl")
|
fmt.Fprintln(os.Stderr, "URL: https://git.arav.su/Arav/mccl")
|
||||||
fmt.Fprintln(os.Stderr, "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.")
|
fmt.Fprintln(os.Stderr, "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.")
|
||||||
}
|
}
|
||||||
@ -98,11 +97,7 @@ func usage() {
|
|||||||
" -U,--uuid UUID\n"+
|
" -U,--uuid UUID\n"+
|
||||||
" player's account UUID (all zeroes by default)\n"+
|
" player's account UUID (all zeroes by default)\n"+
|
||||||
" --java-Xmx memory\n"+
|
" --java-Xmx memory\n"+
|
||||||
" java's -Xmx param, e.g. 4G\n"+
|
" java's -Xmx param, e.g. 4G")
|
||||||
" --profile\n"+
|
|
||||||
" load/save username, UUID, and -Xmx from/to a file.\n"+
|
|
||||||
" Once saved, you can ommit -u,-U and --java-Xmx,\n"+
|
|
||||||
" since they will be stored in", mcclprofile.ProfileFileName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type arguments struct {
|
type arguments struct {
|
||||||
@ -203,8 +198,6 @@ func parseArguments(args []string) (parsed arguments, err error) {
|
|||||||
}
|
}
|
||||||
parsed.JavaXmx = args[i+1]
|
parsed.JavaXmx = args[i+1]
|
||||||
i++
|
i++
|
||||||
case "--profile":
|
|
||||||
parsed.UseProfile = true
|
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("an unknown option %s was provided", args[i])
|
err = fmt.Errorf("an unknown option %s was provided", args[i])
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user