1 Commits

Author SHA1 Message Date
8149d07619 homebrew 2025-06-16 14:00:59 -07:00
9 changed files with 50 additions and 147 deletions

View File

@ -1,6 +1,8 @@
class Epochcli < Formula class Epochcli < Formula
desc "Updater and Launcher for Epoch" desc "Updater and Launcher for Epoch"
homepage "https://git.burkey.co/eburk/epochcli/src/branch/master" homepage "https://git.burkey.co/eburk/epochcli/src/branch/master"
url "https://git.burkey.co/eburk/epochcli/archive/0.0.2.tar.gz"
sha256 "192ae687256a33468e97c7116177b65fb42b42a66138f61f24b93b76f4048f1c"
license "ISC" license "ISC"
head "https://git.burkey.co/eburk/epochcli.git", branch: "master" head "https://git.burkey.co/eburk/epochcli.git", branch: "master"

View File

@ -1,36 +1,15 @@
# epochcli # epochcli
CLI tool for updating and launching [Project Epoch](https://www.project-epoch.net/) on Windows, Linux and macOS. CLI tool for updating and launching [Project Epoch](https://www.project-epoch.net/) on Linux & macOS.
## 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.
### 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.
You can easily install with homebrew or build from source yourself. I dont have time to setup codesigning right now so there are no binaries provided for macOS. For homebrew, do the following:
```shell
brew tap eburk/epochcli https://git.burkey.co/eburk/epochcli
brew install --HEAD epochcli
# To update
brew upgrade epochcli --fetch-HEAD
```
### Windows
Download and extract the latest binary from the [releases](https://git.burkey.co/eburk/epochcli/releases) page or build from source yourself, then copy `epochcli.exe` to a location of your choice. To make it easy, just use the same folder as your Wow game files.
## Setup ## Setup
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`.
1. 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` For macOS, I've found the best way to run the client 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.
2. You can now use `epochcli` as a standalone updater, but it can also act as a launcher based on your configuration. You can always run `epochcli -c` to redo the configuration, or edit the config file manually
1. Download and extract the latest binary from the [releases](https://git.burkey.co/eburk/epochcli/releases) page.
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 ## Usage
``` ```
@ -43,4 +22,4 @@ Download and extract the latest binary from the [releases](https://git.burkey.co
## Issues ## Issues
If you have any issues, [email me](mailto:epochcli@burkey.co) or send a private message to `Battlehammer` on the Epoch discord If you have any issues, [email me](mailto:evan@burkey.co) or ping `Battlehammer` on the Epoch discord

View File

@ -4,10 +4,7 @@ import (
"fmt" "fmt"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"os" "os"
"path"
"path/filepath" "path/filepath"
"runtime"
"strings"
) )
type Config struct { type Config struct {
@ -24,11 +21,10 @@ const (
var cfgPath string var cfgPath string
func setupConfig(rerun bool) (*Config, error) { func setupConfig(rerun bool) (*Config, error) {
home, err := os.UserHomeDir() home := os.Getenv("HOME")
if err != nil { if home == "" {
return nil, fmt.Errorf("unable to determine home directory: %v", err) return nil, fmt.Errorf("$HOME environment variable not set")
} }
cfgPath = filepath.Join(home, ".config", configDirName, configName)
newConfig := Config{ newConfig := Config{
WowDir: defaultWowPath, WowDir: defaultWowPath,
@ -36,56 +32,37 @@ func setupConfig(rerun bool) (*Config, error) {
EnableLauncher: false, EnableLauncher: false,
} }
cfgPath = filepath.Join(home, ".config", configDirName, configName)
_, statErr := os.Stat(cfgPath) _, statErr := os.Stat(cfgPath)
if rerun || os.IsNotExist(statErr) { if rerun || os.IsNotExist(statErr) {
fmt.Println("Enter the path to your Wow directory below:") fmt.Println("Enter the path to your Wow directory below:")
var s string _, err := fmt.Scanln(&newConfig.WowDir)
_, err = fmt.Scanln(&s)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read input: %v", err) return nil, err
} }
newConfig.WowDir = strings.TrimSpace(s)
for { 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): ") 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): ")
_, err = fmt.Scanf("%s", &s) var s string
_, err := fmt.Scanf("%s", &s)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read input: %v", err) return nil, err
} }
s = strings.TrimSpace(s)
if s == "y" || s == "Y" { if s == "y" || s == "Y" {
newConfig.EnableLauncher = true newConfig.EnableLauncher = true
if runtime.GOOS == "windows" {
newConfig.LaunchCmd = path.Join(newConfig.WowDir, "Project-Epoch.exe")
exePath, err := os.Executable()
if err != nil {
fmt.Println("unable to create desktop shortcut: ", err)
break
}
err = makeLink(exePath, path.Join(home, "Desktop", "Project-Epoch.lnk"))
if err != nil {
fmt.Println("unable to create desktop shortcut: ", err)
}
break
} 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.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("> ") fmt.Printf("> ")
_, err = fmt.Scanf("%s", &s) _, err = fmt.Scanf("%s", &s)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read input: %v", err) return nil, err
} }
s = strings.TrimSpace(s)
if s != "" { if s != "" {
newConfig.LaunchCmd = s newConfig.LaunchCmd = s
} }
break break
} }
}
if s == "n" || s == "N" { if s == "n" || s == "N" {
break break
@ -94,26 +71,23 @@ func setupConfig(rerun bool) (*Config, error) {
fmt.Println("Please enter a valid value of either 'y' or 'n'") fmt.Println("Please enter a valid value of either 'y' or 'n'")
} }
err = os.MkdirAll(filepath.Join(home, ".config", configDirName), 0755) os.MkdirAll(filepath.Join(home, ".config", configDirName), 0755)
if err != nil {
return nil, fmt.Errorf("unable to create config directory: %v", err)
}
file, err := os.Create(cfgPath) file, err := os.Create(cfgPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create config file: %v", err) return nil, err
} }
defer file.Close() defer file.Close()
encoder := toml.NewEncoder(file) encoder := toml.NewEncoder(file)
if err = encoder.Encode(newConfig); err != nil { if err = encoder.Encode(newConfig); err != nil {
return nil, fmt.Errorf("unable to encode config file: %v", err) return nil, err
} }
fmt.Println("Created new config at ", cfgPath) fmt.Println("Created new config at ", cfgPath)
} }
_, err = toml.DecodeFile(cfgPath, &newConfig) _, err := toml.DecodeFile(cfgPath, &newConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,8 +0,0 @@
//go:build linux || darwin
package main
func makeLink(src, dst string) error {
// Noop on unix
return nil
}

View File

@ -1,36 +0,0 @@
package main
import (
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"runtime"
)
func makeLink(src, dst string) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_SPEED_OVER_MEMORY)
if err != nil {
return err
}
oleShellObject, err := oleutil.CreateObject("WScript.Shell")
if err != nil {
return err
}
defer oleShellObject.Release()
wshell, err := oleShellObject.QueryInterface(ole.IID_IDispatch)
if err != nil {
return err
}
defer wshell.Release()
cs, err := oleutil.CallMethod(wshell, "CreateShortcut", dst)
if err != nil {
return err
}
idispatch := cs.ToIDispatch()
oleutil.PutProperty(idispatch, "TargetPath", src)
oleutil.CallMethod(idispatch, "Save")
return nil
}

7
go.mod
View File

@ -2,9 +2,4 @@ module git.burkey.co/eburk/epochcli
go 1.24.3 go 1.24.3
require ( require github.com/BurntSushi/toml v1.5.0
github.com/BurntSushi/toml v1.5.0
github.com/go-ole/go-ole v1.3.0
)
require golang.org/x/sys v0.1.0 // indirect

4
go.sum
View File

@ -1,6 +1,2 @@
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= 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/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -70,9 +70,10 @@ func main() {
} }
fmt.Println("Starting Epoch...") fmt.Println("Starting Epoch...")
if runtime.GOOS == "darwin" { switch runtime.GOOS {
case "darwin":
exec.Command("open", config.LaunchCmd).Run() exec.Command("open", config.LaunchCmd).Run()
} else { case "linux":
exec.Command(config.LaunchCmd).Run() exec.Command(config.LaunchCmd).Run()
} }
} }

View File

@ -3,20 +3,20 @@
set -e set -e
rm -f ./*.tar.gz rm -f ./*.tar.gz
rm -f ./*.zip rm -rf bin
GOOS=linux GOARCH=amd64 go build -o epochcli mkdir bin
tar czvf epochcli-linux-amd64.tar.gz epochcli
rm ./epochcli
GOOS=linux GOARCH=arm64 go build -o epochcli GOOS=linux GOARCH=amd64 go build -o bin/epochcli-linux-amd64
tar czvf epochcli-linux-arm64.tar.gz epochcli tar czvf epochcli-linux-amd64.tar.gz bin/epochcli-linux-amd64
rm ./epochcli
GOOS=windows GOARCH=amd64 go build -o epochcli.exe GOOS=linux GOARCH=arm64 go build -o bin/epochcli-linux-arm64
zip -j epochcli-windows-amd64.zip epochcli.exe tar czvf epochcli-linux-arm64.tar.gz bin/epochcli-linux-arm64
rm ./epochcli.exe
GOOS=windows GOARCH=arm64 go build -o epochcli.exe GOOS=darwin GOARCH=arm64 go build -o bin/epochcli-darwin-arm64
zip -j epochcli-windows-arm64.zip epochcli.exe tar czvf epochcli-darwin-arm64.tar.gz bin/epochcli-darwin-arm64
rm ./epochcli.exe
GOOS=darwin GOARCH=amd64 go build -o bin/epochcli-darwin-amd64
tar czvf epochcli-darwin-amd64.tar.gz bin/epochcli-darwin-amd64
rm -rf bin