Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
Added auto restart on quit (#135)
Browse files Browse the repository at this point in the history
* fixed value like 0.1

* initial working
  • Loading branch information
JensvandeWiel authored Nov 22, 2023
1 parent e8f3aa6 commit 0048a87
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 129 deletions.
33 changes: 30 additions & 3 deletions frontend/src/pages/Server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,35 @@ export const Server = ({id, className}: Props) => {
}, [serv]);

useEffect(() => {
EventsOn("onServerExit", () => setServerStatus(false))
EventsOn("onServerExit", (id) => {
if (id === serv.id) {
console.log("server stopped")
setServerStatus(false)
}
})
return () => EventsOff("onServerExit")
}, []);
useEffect(() => {
EventsOn("onServerStart", (id) => {
if (id === serv.id) {
console.log("server started")
setServerStatus(true)
}
})
return () => EventsOff("onServerStart")
}, []);
useEffect(() => {
EventsOn("RestartServer", (id) => {
StartServer(id).catch((err) => {addAlert(err, "danger"); console.error(err)}).then(() => setTimeout(function () {
GetServerStatus(id).catch((reason) => {console.error("serverstatus: " + reason); addAlert(reason, "danger")}).then((s) => {
if (typeof s === "boolean") {
setServerStatus(s)
}
})
}, 200))
})
return () => EventsOff("RestartServer")
}, []);

useEffect(() => {
if (serv.id >= 0) {
Expand Down Expand Up @@ -125,13 +151,14 @@ export const Server = ({id, className}: Props) => {

function startServer() {
StartServer(serv.id).catch((err) => {addAlert(err, "danger"); console.error(err)}).then(() => setTimeout(function () {
refreshServerStatus()
setServerStatus(true)
//refreshServerStatus()
}, 200))
}

function onServerStopButtonClicked() {
addAlert("Stopping server...", "neutral")
StopServer(serv.id).then(() => addAlert("Stopped server", "success")).catch((err) => addAlert("error stopping server: " + err, "danger"));
StopServer(serv.id).then(() => {addAlert("Stopped server", "success"); setServerStatus(false)}).catch((err) => addAlert("error stopping server: " + err, "danger"));
}

function onServerForceStopButtonClicked() {
Expand Down
144 changes: 72 additions & 72 deletions frontend/src/pages/server/Multipliers.tsx

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ module github.com/JensvandeWiel/ArkAscendedServerManager
go 1.18

require (
github.com/StackExchange/wmi v1.2.1
github.com/adrg/xdg v0.4.0
github.com/go-ini/ini v1.67.0
github.com/gorcon/rcon v1.3.4
github.com/hashicorp/go-version v1.6.0
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
github.com/jensvandewiel/gosteamcmd v0.1.2
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
github.com/sethvargo/go-password v0.2.0
github.com/sqweek/dialog v0.0.0-20220809060634-e981b270ebbf
github.com/wailsapp/wails/v2 v2.6.0
golang.org/x/sys v0.13.0
golang.org/x/sys v0.14.0
)

require (
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
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/UserExistsError/conpty v0.1.1 h1:cHDsU/XeoeDAQmVvCTV53SrXLG39YJ4++Pp3iAi1gXE=
Expand All @@ -11,6 +13,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
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=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
Expand All @@ -25,8 +28,6 @@ github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4P
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/jensvandewiel/gosteamcmd v0.1.2 h1:NHichoj0v3GvSVN2Fn36dSOLosHAytpaKnLnDHTMQPI=
github.com/jensvandewiel/gosteamcmd v0.1.2/go.mod h1:Y7hP+iXFIOs8II68VrbdR+W1wwpB8LXYe9lfgjyTLIw=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19 h1:WjT3fLi9n8YWh/Ih8Q1LHAPsTqGddPcHqscN+PJ3i68=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/labstack/echo/v4 v4.11.2 h1:T+cTLQxWCDfqDEoydYm5kCobjmHwOwcv4OJAPHilmdE=
github.com/labstack/echo/v4 v4.11.2/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
Expand Down Expand Up @@ -87,6 +88,7 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQz
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -99,8 +101,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.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=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
Expand Down
63 changes: 63 additions & 0 deletions server/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package server

import (
"fmt"
"github.com/StackExchange/wmi"
"io"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
runtime2 "runtime"
"strconv"
"strings"

Expand Down Expand Up @@ -271,3 +274,63 @@ func ensureFilePath(filePath string) error {
// File already exists
return nil
}

type Win32_Process struct {
ProcessID uint32
ExecutablePath *string
}

func IsProcessRunningWithPath(processPath string) (bool, error) {
var processes []Win32_Process

// Use WMI to query processes
query := "SELECT ProcessID, ExecutablePath FROM Win32_Process"
err := wmi.QueryNamespace(query, &processes, `root\CIMv2`)
if err != nil {
return false, err
}

for _, process := range processes {
if process.ExecutablePath != nil && strings.EqualFold(filepath.Clean(filepath.FromSlash(*process.ExecutablePath)), filepath.Clean(filepath.FromSlash(processPath))) {
return true, nil
}
}

return false, nil
}

func GetProcessPid(processPath string) (uint32, error) {
var processes []Win32_Process

// Use WMI to query processes
query := "SELECT ProcessID, ExecutablePath FROM Win32_Process"
err := wmi.QueryNamespace(query, &processes, `root\CIMv2`)
if err != nil {
return 0, err
}

for _, process := range processes {
if process.ExecutablePath != nil && strings.EqualFold(filepath.Clean(filepath.FromSlash(*process.ExecutablePath)), filepath.Clean(filepath.FromSlash(processPath))) {
return process.ProcessID, nil
}
}

return 0, nil
}

func KillProcessUsingPid(pid uint32) error {
var cmd *exec.Cmd

switch runtime2.GOOS {
case "windows":
cmd = exec.Command("taskkill", "/F", "/PID", strconv.FormatUint(uint64(pid), 10))
default:
cmd = exec.Command("kill", "-9", strconv.FormatUint(uint64(pid), 10))
}

if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to kill process: %v", err)
}

return nil
}
69 changes: 32 additions & 37 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import (
"time"

"github.com/JensvandeWiel/ArkAscendedServerManager/helpers"
"github.com/keybase/go-ps"
"github.com/wailsapp/wails/v2/pkg/runtime"
)

// Server contains the server "stuff"

type Server struct {
Command *exec.Cmd `json:"-"`
ctx context.Context
helpers *helpers.HelpersController
Command *exec.Cmd `json:"-"`
ctx context.Context
helpers *helpers.HelpersController
stopSignal chan bool

//PREFERENCES

Expand Down Expand Up @@ -127,13 +127,17 @@ func (s *Server) Start() error {
}
go func() {
_ = s.Command.Wait()

runtime.EventsEmit(s.ctx, "onServerExit", s.Id)
if s.DiscordWebHookEnabled {
err := helpers.SendToDiscord(time.Now().Format(time.RFC822)+" ("+s.ServerAlias+") Server has stopped", s.DiscordWebHook)
if err != nil {
runtime.LogError(s.ctx, "Error sending message to discord: "+err.Error())
}

// Check if the server exited unexpectedly (not through Stop or ForceStop)
select {
case <-s.stopSignal:
// Server was intentionally stopped, do not restart
runtime.LogInfo(s.ctx, "Server was intentionally stopped. Not restarting.")
default:
// Restart the server
time.Sleep(2 * time.Second)
runtime.EventsEmit(s.ctx, "RestartServer", s.Id)
}
}()
}
Expand All @@ -156,14 +160,17 @@ func (s *Server) ForceStop() error {
if s.Command.Process == nil {
return fmt.Errorf("error stopping server: server is not running")
}

if !s.IsServerRunning() {
return fmt.Errorf("error stopping server: server is not running")
}

err := s.Command.Process.Kill()
if err != nil {
if pid, err := GetProcessPid(path.Join(s.ServerPath, "ShooterGame\\Binaries\\Win64\\ArkAscendedServer.exe")); err != nil {
return fmt.Errorf("error stopping server: %v", err)
} else {
err := KillProcessUsingPid(pid)
if err != nil {
return fmt.Errorf("error killing process: %v", err)
}
}

if s.DiscordWebHookEnabled {
Expand All @@ -173,6 +180,8 @@ func (s *Server) ForceStop() error {
}
}

s.stopSignal <- true

return nil
}

Expand All @@ -187,47 +196,33 @@ func (s *Server) Stop() error {

err := s.SaveWorld()
if err != nil {
return err
return fmt.Errorf("error sending save world command: %v", err)
}
_, err = s.helpers.SendRconCommand("doexit", s.IpAddress, s.RCONPort, s.AdminPassword)
if err != nil {
return err
return fmt.Errorf("error sending exit command: %v", err)
}

s.stopSignal <- true

return nil
}

//TODO BROKEN

// GetServerStatus returns the status of the server.
func (s *Server) IsServerRunning() bool {

if s.Command == nil {
return false
}

if s.Command.Process == nil {
//THIS CHECK IS ONLY HERE BECAUSE WE DO NOT OFFICIALY SUPPORT MANAGING SERVERS THAT ARE ALREADY STARTED (WILL BE POSSIBLE IN THE FUTURE)
if s.Command == nil || s.Command.Process == nil {
return false
}

// Retrieve a list of all processes.
processList, err := ps.Processes()
isRunning, err := IsProcessRunningWithPath(path.Join(s.ServerPath, "ShooterGame\\Binaries\\Win64\\ArkAscendedServer.exe"))
if err != nil {
return false
}

// Iterate through the list of processes and check if the specified PID exists.
processFound := false
for _, process := range processList {
if process.Pid() == s.Command.Process.Pid {
processFound = true
break
}
}

if processFound {
return true
} else {
return false
}
return isRunning
}

// returns questionamrk arguments for the server and dash arguments for the server
Expand Down
24 changes: 13 additions & 11 deletions server/server_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,7 @@ func (c *ServerController) SaveServer(server Server) error {

//TODO Make sure oldserv members get passed over to new instance since frontend will change all members even Command (which should not be updated by the frontend)

oldServ, err := c.getServer(server.Id, true)
if err != nil {
newErr := fmt.Errorf("Error saving server: " + err.Error())
runtime.LogError(c.ctx, newErr.Error())
return newErr
}

server.Command = oldServ.Command
server.ctx = c.ctx

err = c.saveServer(&server)
err := c.saveServer(&server)
if err != nil {
newErr := fmt.Errorf("Error saving server: " + err.Error())
runtime.LogError(c.ctx, newErr.Error())
Expand Down Expand Up @@ -241,6 +231,8 @@ func (c *ServerController) getServerFromDir(id int, shouldReturnNew bool) (Serve
return Server{}, fmt.Errorf("Parsing server instance failed: " + err.Error())
}

serv.stopSignal = make(chan bool)

return serv, nil
}

Expand Down Expand Up @@ -335,6 +327,16 @@ func (c *ServerController) saveServer(server *Server) error {
if err := CheckIfServerCorrect(server); err != nil {
return fmt.Errorf("Parsing server instance failed: " + err.Error())
}
oldServ, err := c.getServer(server.Id, true)
if err != nil {
newErr := fmt.Errorf("Error saving server: " + err.Error())
runtime.LogError(c.ctx, newErr.Error())
return newErr
}

server.Command = oldServ.Command
server.ctx = c.ctx
server.stopSignal = oldServ.stopSignal
c.Servers[server.Id] = server
serverDir := path.Join(c.serverDir, strconv.Itoa(server.Id))

Expand Down

0 comments on commit 0048a87

Please sign in to comment.