diff --git a/README.md b/README.md index 134d1e3..c7da1be 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,25 @@ CLI tool for updating and launching [Project Epoch](https://www.project-epoch.net/) on Linux & macOS. -## TODO +## Setup Instructions +For Linux, a `wine` prefix with `dxvk` installed is sufficient, or you can use something like Lutris or faugus-launcher without the launcher functionality in `epochcli` -- improve config generation -- add ability to launch Epoch after an update +For macOS, the [Kegworks Wineskin port](https://github.com/Kegworks-App/Kegworks) works great (tutorial coming in the future...) -## Instructions -For Linux, a `wine` prefix with `dxvk` installed works great, or you can use something like Lutris or Faugus-launcher - -For macOS, the [Kegworks Wineskin port](https://github.com/Kegworks-App/Kegworks) works great - -2. Install `epochcli` by either - 1. Download the latest binary from the [releases](https://git.burkey.co/eburk/epochcli/releases) page +1. Install `epochcli` by either + 1. Downloading the latest binary from the [releases](https://git.burkey.co/eburk/epochcli/releases) page 2. If you have the `go` toolchain installed, you can run `go install git.burkey.co/eburk/epochcli` to install to your `$GOROOT` 3. Compile the source yourself -3. Run `epochcli` once. This will create a config file at `$HOME/.config/epochcli/config.toml`. Update the variables with the appropriate information -4. Run `epochcli` again. It will download the patch files from Epoch's servers to your Wow directory and start the game. You can also pass the -u switch to only update if you use a launcher like Lutris, etc +2. Run `epochcli`. You will be taken through a setup process that configures the program and creates a config file at `$HOME/.config/epochcli/config.toml` +3. You can now use `epochcli` as just a standalone updater or also a launcher based on your configuration. You can always run `epochcli -c` to redo the configuration or edit the config file manually + +## Usage +``` +> ./epochcli -h + -c Runs config configuration step. Overrides the config file + -h Print help + -u Ignore EnableLauncher setting in config and only runs an update. Does nothing if EnableLauncher is false +``` ## Issues diff --git a/config.go b/config.go index 36b39db..d262c1f 100644 --- a/config.go +++ b/config.go @@ -1,15 +1,18 @@ package main import ( + "errors" "fmt" "github.com/BurntSushi/toml" + "github.com/sqweek/dialog" "os" "path/filepath" ) type Config struct { - WowDir string - LaunchCmd string + WowDir string + LaunchCmd string + EnableLauncher bool } const ( @@ -19,20 +22,64 @@ const ( var cfgPath string -func setupConfig() (*Config, error) { +func setupConfig(rerun bool) (*Config, error) { home := os.Getenv("HOME") if home == "" { return nil, fmt.Errorf("$HOME environment variable not set") } newConfig := Config{ - WowDir: defaultWowPath, - LaunchCmd: defaultLaunchCmd, + WowDir: defaultWowPath, + LaunchCmd: defaultLaunchCmd, + EnableLauncher: false, } cfgPath = filepath.Join(home, ".config", configDirName, configName) - if _, statErr := os.Stat(cfgPath); os.IsNotExist(statErr) { + _, statErr := os.Stat(cfgPath) + if rerun || os.IsNotExist(statErr) { + fmt.Println("Press any key to open a file window and select your wow directory") + var r rune + _, _ = fmt.Scanf("%c", &r) + + var err error + newConfig.WowDir, err = dialog.Directory().Title("Select your wow directory").Browse() + if err != nil { + if errors.Is(err, dialog.ErrCancelled) { + return nil, fmt.Errorf("cancelled dialog box, exiting") + } + return nil, err + } + + for { + fmt.Printf("Do you want to use epochcli to launch Wow? Select No if you plan on using a launcher tool like Lutris (y/n): ") + var s string + _, err = fmt.Scanf("%s", &s) + if err != nil { + return nil, err + } + + if s == "y" || s == "Y" { + newConfig.EnableLauncher = true + fmt.Println("Enter your launch command to start Wow below. If you would rather configure this later in the configuration file, just press Enter") + fmt.Printf("> ") + _, err = fmt.Scanf("%s", &s) + if err != nil { + return nil, err + } + if s != "" { + newConfig.LaunchCmd = s + } + break + } + + if s == "n" || s == "N" { + break + } + + fmt.Println("Please enter a valid value of either 'y' or 'n'") + } + os.MkdirAll(filepath.Join(home, ".config", configDirName), 0755) file, err := os.Create(cfgPath) @@ -46,8 +93,7 @@ func setupConfig() (*Config, error) { return nil, err } - fmt.Printf("Created new config at %s, edit it before running the launcher again\n", cfgPath) - os.Exit(0) + fmt.Println("Created new config at ", cfgPath) } _, err := toml.DecodeFile(cfgPath, &newConfig) diff --git a/go.mod b/go.mod index 7a1076c..e5f6dd7 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module git.burkey.co/eburk/epochcli go 1.24.3 -require github.com/BurntSushi/toml v1.5.0 +require ( + github.com/BurntSushi/toml v1.5.0 + github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 +) + +require github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect diff --git a/go.sum b/go.sum index ff7fd09..5b20de6 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I= +github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 h1:2JL2wmHXWIAxDofCK+AdkFi1KEg3dgkefCsm7isADzQ= +github.com/sqweek/dialog v0.0.0-20240226140203-065105509627/go.mod h1:/qNPSY91qTz/8TgHEMioAUc6q7+3SOybeKczHMXFcXw= diff --git a/main.go b/main.go index 4fd951f..5f0d41c 100644 --- a/main.go +++ b/main.go @@ -25,9 +25,11 @@ func main() { var ( helpFlag bool updateOnlyFlag bool + rerunConfig bool ) flag.BoolVar(&helpFlag, "h", false, "Print help") - flag.BoolVar(&updateOnlyFlag, "u", false, "Only update the client, do not launch") + flag.BoolVar(&updateOnlyFlag, "u", false, "Ignore EnableLauncher setting in config and only runs an update. Does nothing if EnableLauncher is false") + flag.BoolVar(&rerunConfig, "c", false, "Runs config configuration step. Overrides the config file") flag.Parse() if helpFlag { @@ -36,7 +38,7 @@ func main() { os.Exit(0) } - config, err := setupConfig() + config, err := setupConfig(rerunConfig) if err != nil { log.Fatal(err) } @@ -45,33 +47,37 @@ func main() { log.Fatalf("WowDir in %s is still the default setting, exiting", cfgPath) } - count, err := downloadUpdate(config) + updated, current, err := downloadUpdate(config) if err != nil { log.Fatal(err) } - fmt.Printf("Updated %d files\n", count) + fmt.Printf("Updated %d files\n", updated) + if current > 0 { + fmt.Printf("%d files are already up to date\n", current) + } if updateOnlyFlag { os.Exit(0) } - if config.LaunchCmd == defaultLaunchCmd { - log.Fatalf("LaunchCmd in %s is still the default setting, exiting\n", cfgPath) - } + if config.EnableLauncher { + if config.LaunchCmd == defaultLaunchCmd { + log.Fatalf("LaunchCmd in %s is still the default setting, exiting\n", cfgPath) + } - fmt.Printf("Starting Epoch....\n", count) - - switch runtime.GOOS { - case "darwin": - exec.Command("open", config.LaunchCmd).Run() - case "linux": - exec.Command(config.LaunchCmd).Run() + fmt.Println("Starting Epoch...") + switch runtime.GOOS { + case "darwin": + exec.Command("open", config.LaunchCmd).Run() + case "linux": + exec.Command(config.LaunchCmd).Run() + } } } -func downloadUpdate(config *Config) (int, error) { - var c int +func downloadUpdate(config *Config) (int, int, error) { + var updateCount, currentCount int manifest, err := getManifest() if err != nil { @@ -91,11 +97,12 @@ func downloadUpdate(config *Config) (int, error) { if _, err = os.Stat(localPath); err == nil { data, err := os.ReadFile(localPath) if err != nil { - return c, err + return updateCount, currentCount, err } hashBytes := md5.Sum(data) hash := hex.EncodeToString(hashBytes[:]) if hash == file.Hash { + currentCount += 1 continue } } @@ -104,26 +111,26 @@ func downloadUpdate(config *Config) (int, error) { outFile, err := os.Create(localPath) if err != nil { - return c, err + return updateCount, currentCount, err } defer outFile.Close() resp, err := http.Get(file.URL) if err != nil { - return c, err + return updateCount, currentCount, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return c, fmt.Errorf("failed to download update from %s, status code: %d", file.URL, resp.StatusCode) + return updateCount, currentCount, fmt.Errorf("failed to download update from %s, status code: %d", file.URL, resp.StatusCode) } _, err = io.Copy(outFile, resp.Body) if err != nil { - return c, err + return updateCount, currentCount, err } - c += 1 + updateCount += 1 } - return c, nil + return updateCount, currentCount, nil } diff --git a/release.sh b/release.sh index 4dbc496..18f4d2f 100755 --- a/release.sh +++ b/release.sh @@ -3,15 +3,13 @@ set -e rm -f ./*.tar.gz +rm -rf bin mkdir bin GOOS=linux GOARCH=amd64 go build -o bin/epochcli-linux-amd64 tar czvf epochcli-linux-amd64.tar.gz bin/epochcli-linux-amd64 -GOOS=linux GOARCH=arm64 go build -o bin/epochcli-linux-arm64 -tar czvf epochcli-linux-arm64.tar.gz bin/epochcli-linux-arm64 - GOOS=darwin GOARCH=amd64 go build -o bin/epochcli-darwin-amd64 tar czvf epochcli-darwin-amd64.tar.gz bin/epochcli-darwin-amd64