diff --git a/console/console.go b/console/console.go new file mode 100644 index 0000000..beec161 --- /dev/null +++ b/console/console.go @@ -0,0 +1,14 @@ +package console + +import "io" + +func New(exePath string, args []string, stdout io.Writer) *Console { + p := NewParser() + + return &Console{ + commandLine: exePath, + stdout: stdout, + args: args, + Parser: p, + } +} diff --git a/console/console_unix.go b/console/console_unix.go new file mode 100644 index 0000000..97410b3 --- /dev/null +++ b/console/console_unix.go @@ -0,0 +1,59 @@ +//go:build unix + +package console + +import ( + "fmt" + "io" + "os/exec" +) + +type Console struct { + stdout io.Writer // stdout is the writer where the output will be written to. + commandLine string // commandLine is the command that will be executed. + proc *exec.Cmd + exitCode uint32 + args []string + Parser *Parser +} + +// Run executes the command in steamcmd and returns the exit code. Exit code does not need to be 0 to return no errors (error is for executing the pseudoconsole) +func (c *Console) Run() (uint32, error) { + var err error + + if c.commandLine == "" { + _, err := exec.LookPath(c.commandLine) + if err != nil { + return 1, fmt.Errorf("Steamcmd not found: %v\n", err) + } + } + + var a []string + a = append(a, c.commandLine) + a = append(a, c.args...) + + c.proc = exec.Command("sh", a...) + if err != nil { + return 1, err + } + + d := &duplicateWriter{ + writer1: c.Parser, + writer2: c.stdout, + } + + c.proc.Stdout = d + c.proc.Stderr = d + + err = c.proc.Run() + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + c.exitCode = uint32(exitErr.ExitCode()) + } else { + return 1, err + } + } + c.exitCode = uint32(c.proc.ProcessState.ExitCode()) + + return c.exitCode, nil +} diff --git a/console/console_windows.go b/console/console_windows.go index 9e9e2d5..23c42b2 100644 --- a/console/console_windows.go +++ b/console/console_windows.go @@ -4,32 +4,38 @@ package console import ( "context" + "fmt" "github.com/UserExistsError/conpty" "io" + "os/exec" + "strings" ) type Console struct { stdout io.Writer // stdout is the writer where the output will be written to. commandLine string // commandLine is the command that will be executed. conPTY *conpty.ConPty + args []string exitCode uint32 Parser *Parser } -func New(exePath string, stdout io.Writer) *Console { - p := NewParser() - - return &Console{ - commandLine: exePath, - stdout: stdout, - Parser: p, - } -} - // Run executes the command in steamcmd and returns the exit code. Exit code does not need to be 0 to return no errors (error is for executing the pseudoconsole) func (c *Console) Run() (uint32, error) { var err error - c.conPTY, err = conpty.Start(c.commandLine) + + if c.commandLine == "" { + _, err := exec.LookPath(c.commandLine) + if err != nil { + return 1, fmt.Errorf("Steamcmd not found: %v\n", err) + } + } + + var a []string + a = append(a, c.commandLine) + a = append(a, c.args...) + + c.conPTY, err = conpty.Start(c.commandLine + " " + strings.Join(a, " ")) if err != nil { return 1, err } diff --git a/examples/install-rustserver-linux/main.go b/examples/install-rustserver-linux/main.go new file mode 100644 index 0000000..a07d468 --- /dev/null +++ b/examples/install-rustserver-linux/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/jensvandewiel/gosteamcmd" + "github.com/jensvandewiel/gosteamcmd/console" + "os" +) + +func main() { + //this code follows the steps of: https://www.rustafied.com/how-to-host-your-own-rust-server + + prompts := []*gosteamcmd.Prompt{ + gosteamcmd.ForceInstallDir("/home/jens/rustserver"), + gosteamcmd.Login("", ""), + gosteamcmd.AppUpdate(258550, "", true), + } + + cmd := gosteamcmd.New(os.Stdout, prompts, "/mnt/MyProjects/gosteamcmd/steamcmd.sh") + + cmd.Console.Parser.OnInformationReceived = func(action console.Action, progress float64, currentWritten, total uint64) { + println("") + } + cmd.Console.Parser.OnAppInstalled = func(app uint32) { + println("App installed: ", app, " Yay!") + } + + _, err := cmd.Run() + + if err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index c40ca2d..622f8b5 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,9 @@ module github.com/jensvandewiel/gosteamcmd go 1.20 -require github.com/UserExistsError/conpty v0.1.1 +require ( + github.com/UserExistsError/conpty v0.1.1 + github.com/creack/pty v1.1.18 +) -require golang.org/x/sys v0.11.0 // indirect +require golang.org/x/sys v0.13.0 // indirect diff --git a/go.sum b/go.sum index a8545b6..e6f46d8 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ github.com/UserExistsError/conpty v0.1.1 h1:cHDsU/XeoeDAQmVvCTV53SrXLG39YJ4++Pp3iAi1gXE= github.com/UserExistsError/conpty v0.1.1/go.mod h1:PDglKIkX3O/2xVk0MV9a6bCWxRmPVfxqZoTG/5sSd9I= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/steamcmd.go b/steamcmd.go index 57985e1..409f4ef 100644 --- a/steamcmd.go +++ b/steamcmd.go @@ -23,11 +23,12 @@ func New(stdout io.Writer, prompts []*Prompt, steamPath string) *SteamCMD { //prepare command cmd := path(steamPath) + var args []string for _, prompt := range s.prompts { - cmd += " +" + prompt.FullPrompt + args = append(args, "+"+prompt.FullPrompt) } - cmd += " +quit" - s.Console = console.New(cmd, s.Stdout) + args = append(args, "+quit") + s.Console = console.New(cmd, args, s.Stdout) return s }