1 Commits
1.0 ... 0.0.3

Author SHA1 Message Date
8149d07619 homebrew 2025-06-16 14:00:59 -07:00
9 changed files with 39 additions and 136 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.
## 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`.
### Windows 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.
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. 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`
### Linux 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
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 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.
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
```
## First Run
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`
2. 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
``` ```

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

@ -13,10 +13,10 @@ tar czvf epochcli-linux-amd64.tar.gz bin/epochcli-linux-amd64
GOOS=linux GOARCH=arm64 go build -o bin/epochcli-linux-arm64 GOOS=linux GOARCH=arm64 go build -o bin/epochcli-linux-arm64
tar czvf epochcli-linux-arm64.tar.gz bin/epochcli-linux-arm64 tar czvf epochcli-linux-arm64.tar.gz bin/epochcli-linux-arm64
GOOS=windows GOARCH=amd64 go build -o bin/epochcli-windows-amd64 GOOS=darwin GOARCH=arm64 go build -o bin/epochcli-darwin-arm64
zip epochcli-windows-amd64.zip bin/epochcli-windows-amd64 tar czvf epochcli-darwin-arm64.tar.gz bin/epochcli-darwin-arm64
GOOS=windows GOARCH=arm64 go build -o bin/epochcli-windows-arm64 GOOS=darwin GOARCH=amd64 go build -o bin/epochcli-darwin-amd64
zip epochcli-windows-arm64.zip bin/epochcli-windows-arm64 tar czvf epochcli-darwin-amd64.tar.gz bin/epochcli-darwin-amd64
rm -rf bin rm -rf bin