diff --git a/README.md b/README.md index 93c0041..37e6979 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,15 @@ CLI tool for updating and launching [Project Epoch](https://www.project-epoch.ne ## Installing ### Linux + A `wine` prefix with `dxvk` installed is sufficient, or you can use something like Lutris or faugus-launcher and just use `epochcli` for updating. Download and extract the latest binary from the [releases](https://git.burkey.co/eburk/epochcli/releases) page, build from source yourself, or use homebrew from the macOS instructions to install. If you are an Arch Linux (or derivative) user, you can use the provided [AUR package](https://aur.archlinux.org/packages/epochcli) to install +A tutorial for the full setup of Epoch on Arch Linux [can be found here](https://burkey.co/posts/epoch-linux/) + ### macOS For macOS, I've found the best way to run Wow is in a Parallels Win 11 VM. Kegworks, Codeweavers, etc crash when the game starts up and I have not found a good solution so far. Any suggestions would be welcome, see my contact information below. I currently use a Parallels VM and run the Windows version of epochcli inside the VM as an updater and launcher. diff --git a/config.go b/config.go index 1528716..1167408 100644 --- a/config.go +++ b/config.go @@ -13,6 +13,7 @@ import ( type Config struct { WowDir string LaunchCmd string + WinePrefix string EnableLauncher bool } @@ -46,15 +47,18 @@ func setupConfig(rerun bool) (*Config, error) { } newConfig.WowDir = strings.TrimSpace(s) + fmt.Println() p, err := promptYesNo(fmt.Sprintf("Do you want to use epochcli to launch Wow? Select No if you plan on using a launcher tool like Lutris (y/n): ")) if err != nil { return nil, err } + fmt.Println() + if p { newConfig.EnableLauncher = true + newConfig.LaunchCmd = path.Join(newConfig.WowDir, "Project-Epoch.exe") if runtime.GOOS == "windows" { - newConfig.LaunchCmd = path.Join(newConfig.WowDir, "Project-Epoch.exe") exePath, err := os.Executable() if err != nil { return nil, fmt.Errorf("unable to create desktop shortcut: %v", err) @@ -64,18 +68,22 @@ func setupConfig(rerun bool) (*Config, error) { return nil, fmt.Errorf("unable to create desktop shortcut: %v", err) } } else { - 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) + s, err = input("Enter your wine prefix. Leave blank if you do not need to set WINEPREFIX") if err != nil { - return nil, fmt.Errorf("unable to read input: %v", err) + return nil, err } - s = strings.TrimSpace(s) + fmt.Println() - if s != "" { - newConfig.LaunchCmd = s + newConfig.WinePrefix = s + newConfig.LaunchCmd = "wine " + newConfig.LaunchCmd + + fmt.Println("Your launch command has been set to the following:") + if s == "" { + fmt.Printf("wine %s\n", newConfig.LaunchCmd) + } else { + fmt.Printf("WINEPREFIX=%s wine %s\n", newConfig.WinePrefix, newConfig.LaunchCmd) } + fmt.Printf("Modify the configuration file at %s if you need to customize it\n\n", cfgPath) } } @@ -95,7 +103,7 @@ func setupConfig(rerun bool) (*Config, error) { return nil, fmt.Errorf("unable to encode config file: %v", err) } - fmt.Println("Created new config at ", cfgPath) + fmt.Printf("Created new config at %s\n\n", cfgPath) } _, err = toml.DecodeFile(cfgPath, &newConfig) @@ -126,3 +134,15 @@ func promptYesNo(prompt string) (bool, error) { fmt.Println("Please enter a valid value of either 'y' or 'n'") } } + +func input(prompt string) (string, error) { + fmt.Println(prompt) + fmt.Printf("> ") + + var s string + _, err := fmt.Scanf("%s", &s) + if err != nil { + return "", fmt.Errorf("unable to read input: %v", err) + } + return strings.TrimSpace(s), nil +} diff --git a/main.go b/main.go index ea04873..86a30b0 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,7 @@ func main() { } if outOfDate { - fmt.Println("There is a new version of epochcli, please update before running") + fmt.Println("There is a new version of epochcli, you must update before running") os.Exit(1) } @@ -65,9 +65,9 @@ func main() { log.Fatal(err) } - fmt.Printf("%d files updated\n", stats.updated) + fmt.Printf("%d files updated\n\n", stats.updated) if stats.current > 0 { - fmt.Printf("%d files are already up to date\n", stats.current) + fmt.Printf("%d files are already up to date\n\n", stats.current) } if updateOnlyFlag { @@ -80,10 +80,22 @@ func main() { } fmt.Println("Starting Epoch...") + var cmd = strings.Split(config.LaunchCmd, " ") if runtime.GOOS == "darwin" { - exec.Command("open", config.LaunchCmd).Run() - } else { - exec.Command(config.LaunchCmd).Run() + cmd = append([]string{"open"}, cmd...) + } + ex := exec.Command(cmd[0], cmd[1:]...) + cmdStr := strings.Join(cmd, " ") + if config.WinePrefix != "" { + prefix := fmt.Sprintf("WINEPREFIX=%s", config.WinePrefix) + newEnv := append(os.Environ(), prefix) + ex.Env = newEnv + cmdStr = prefix + " " + cmdStr + } + fmt.Println("Running command:", cmdStr) + err = ex.Run() + if err != nil { + log.Fatal(err) } } } @@ -108,7 +120,10 @@ func downloadUpdate(config *Config, force bool) (DownloadStats, error) { localPath := filepath.Join(config.WowDir, path) localDir := filepath.Dir(localPath) if _, err = os.Stat(localDir); os.IsNotExist(err) { - os.MkdirAll(localDir, 0755) + err = os.MkdirAll(localDir, 0755) + if err != nil { + return stats, fmt.Errorf("failed to create directory %s: %v", localDir, err) + } } if !force { @@ -127,32 +142,35 @@ func downloadUpdate(config *Config, force bool) (DownloadStats, error) { } } - fmt.Printf(" %s...\n", localPath) + fmt.Printf("Updating %s...\n", localPath) outFile, err := os.Create(localPath) if err != nil { return stats, err } - defer outFile.Close() for _, url := range []string{file.Urls.Cloudflare, file.Urls.Digitalocean, file.Urls.None} { resp, err := http.Get(url) if err != nil { + outFile.Close() return stats, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { + outFile.Close() return stats, fmt.Errorf("failed to download update from %s, status code: %d", url, resp.StatusCode) } _, err = io.Copy(outFile, resp.Body) if err != nil { + outFile.Close() return stats, err } break } + outFile.Close() stats.updated += 1 }