mirror of
https://github.com/OpenFactorioServerManager/factorio-server-manager.git
synced 2025-01-10 04:19:38 +02:00
Merge pull request #77 from JakenVeina/server-save-on-stop-windows
Stop & Save functionality on Windows, and additional Kill functionality.
This commit is contained in:
commit
f33e540393
8
Makefile
8
Makefile
@ -4,16 +4,18 @@
|
||||
NODE_ENV:=production
|
||||
|
||||
build:
|
||||
# make sure this project is located within GOPATH, I.E. $GOPATH/src/factorio-server-manager
|
||||
|
||||
# Build Linux release
|
||||
mkdir build
|
||||
GOOS=linux GOARCH=amd64 go build -o factorio-server-manager/factorio-server-manager src/*
|
||||
GOOS=linux GOARCH=amd64 go build -o factorio-server-manager/factorio-server-manager factorio-server-manager/src
|
||||
# ui/node_modules/webpack/bin/webpack.js ui/webpack.config.js app/bundle.js --progress --profile --colors
|
||||
cp -r app/ factorio-server-manager/
|
||||
cp conf.json.example factorio-server-manager/conf.json
|
||||
zip -r build/factorio-server-manager-linux-x64.zip factorio-server-manager
|
||||
rm -rf factorio-server-manager
|
||||
# Build Windows release
|
||||
GOOS=windows GOARCH=386 go build -o factorio-server-manager/factorio-server-manager.exe src/*
|
||||
GOOS=windows GOARCH=386 go build -o factorio-server-manager/factorio-server-manager.exe factorio-server-manager/src
|
||||
cp -r app/ factorio-server-manager/
|
||||
cp conf.json.example factorio-server-manager/conf.json
|
||||
zip -r build/factorio-server-manager-windows.zip factorio-server-manager
|
||||
@ -21,7 +23,7 @@ build:
|
||||
|
||||
dev:
|
||||
mkdir dev
|
||||
GOOS=linux GOARCH=amd64 go build -o factorio-server-linux/factorio-server-manager src/*
|
||||
GOOS=linux GOARCH=amd64 go build -o factorio-server-linux/factorio-server-manager factorio-server-manager/src
|
||||
cp -r app/ dev/
|
||||
cp conf.json.example dev/conf.json
|
||||
mv factorio-server-linux/factorio-server-manager dev/factorio-server-manager
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/majormjr/rcon"
|
||||
)
|
||||
@ -225,21 +226,27 @@ func (f *FactorioServer) checkLogError(logline []string) error {
|
||||
}
|
||||
|
||||
func (f *FactorioServer) Stop() error {
|
||||
// TODO: Find an alternative to os.Kill on Windows. os.Interupt
|
||||
// is not implemented. Maps will not be saved.
|
||||
if runtime.GOOS == "windows" {
|
||||
err := f.Cmd.Process.Signal(os.Kill)
|
||||
if err != nil {
|
||||
if err.Error() == "os: process already finished" {
|
||||
f.Running = false
|
||||
return err
|
||||
}
|
||||
log.Printf("Error sending SIGKILLL to Factorio process: %s", err)
|
||||
return err
|
||||
}
|
||||
f.Running = false
|
||||
log.Println("Sent SIGKILL to Factorio process. Factorio forced to exit.")
|
||||
|
||||
// Disable our own handling of CTRL+C, so we don't close when we send it to the console.
|
||||
setCtrlHandlingIsDisabledForThisProcess(true)
|
||||
|
||||
// Send CTRL+C to all processes attached to the console (ourself, and the factorio server instance)
|
||||
sendCtrlCToPid(0)
|
||||
log.Println("Sent SIGINT to Factorio process. Factorio shutting down...")
|
||||
|
||||
// Somehow, the Factorio devs managed to code the game to react appropriately to CTRL+C, including
|
||||
// saving the game, but not actually exit. So, we still have to manually kill the process, and
|
||||
// for extra fun, there's no way to know when the server save has actually completed (unless we want
|
||||
// to inject filesystem logic into what should be a process-level Stop() routine), so our best option
|
||||
// is to just wait an arbitrary amount of time and hope that the save is successful in that time.
|
||||
time.Sleep(2 * time.Second)
|
||||
f.Cmd.Process.Signal(os.Kill)
|
||||
|
||||
// Re-enable handling of CTRL+C after we're sure that the factrio server is shut down.
|
||||
setCtrlHandlingIsDisabledForThisProcess(false)
|
||||
|
||||
f.Running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -262,3 +269,43 @@ func (f *FactorioServer) Stop() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FactorioServer) Kill() error {
|
||||
if runtime.GOOS == "windows" {
|
||||
|
||||
err := f.Cmd.Process.Signal(os.Kill)
|
||||
if err != nil {
|
||||
if err.Error() == "os: process already finished" {
|
||||
f.Running = false
|
||||
return err
|
||||
}
|
||||
log.Printf("Error sending SIGKILL to Factorio process: %s", err)
|
||||
return err
|
||||
}
|
||||
f.Running = false
|
||||
log.Println("Sent SIGKILL to Factorio process. Factorio forced to exit.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := f.Cmd.Process.Signal(os.Kill)
|
||||
if err != nil {
|
||||
if err.Error() == "os: process already finished" {
|
||||
f.Running = false
|
||||
return err
|
||||
}
|
||||
log.Printf("Error sending SIGKILL to Factorio process: %s", err)
|
||||
return err
|
||||
}
|
||||
f.Running = false
|
||||
log.Printf("Sent SIGKILL to Factorio process. Factorio forced to exit.")
|
||||
|
||||
err = f.Rcon.Close()
|
||||
if err != nil {
|
||||
log.Printf("Error close rcon connection: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
10
src/factorio_server_linux.go
Normal file
10
src/factorio_server_linux.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
|
||||
// Stubs for windows-only functions
|
||||
|
||||
func sendCtrlCToPid(pid int) {
|
||||
}
|
||||
|
||||
func setCtrlHandlingIsDisabledForThisProcess(disabled bool) {
|
||||
}
|
41
src/factorio_server_windows.go
Normal file
41
src/factorio_server_windows.go
Normal file
@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sendCtrlCToPid(pid int) {
|
||||
d, e := syscall.LoadDLL("kernel32.dll")
|
||||
if e != nil {
|
||||
log.Fatalf("LoadDLL: %v\n", e)
|
||||
}
|
||||
p, e := d.FindProc("GenerateConsoleCtrlEvent")
|
||||
if e != nil {
|
||||
log.Fatalf("FindProc: %v\n", e)
|
||||
}
|
||||
r, _, e := p.Call(uintptr(syscall.CTRL_C_EVENT), uintptr(pid))
|
||||
if r == 0 {
|
||||
log.Fatalf("GenerateConsoleCtrlEvent: %v\n", e)
|
||||
}
|
||||
}
|
||||
|
||||
func setCtrlHandlingIsDisabledForThisProcess(disabled bool) {
|
||||
disabledInt := 0
|
||||
if(disabled){
|
||||
disabledInt = 1
|
||||
}
|
||||
|
||||
d, e := syscall.LoadDLL("kernel32.dll")
|
||||
if e != nil {
|
||||
log.Fatalf("LoadDLL: %v\n", e)
|
||||
}
|
||||
p, e := d.FindProc("SetConsoleCtrlHandler")
|
||||
if e != nil {
|
||||
log.Fatalf("FindProc: %v\n", e)
|
||||
}
|
||||
r, _, e := p.Call(uintptr(0), uintptr(disabledInt))
|
||||
if r == 0 {
|
||||
log.Fatalf("SetConsoleCtrlHandler: %v\n", e)
|
||||
}
|
||||
}
|
@ -667,6 +667,41 @@ func StopServer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func KillServer(w http.ResponseWriter, r *http.Request) {
|
||||
resp := JSONResponse{
|
||||
Success: false,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
||||
|
||||
if FactorioServ.Running {
|
||||
err := FactorioServ.Kill()
|
||||
if err != nil {
|
||||
log.Printf("Error in kill server handler: %s", err)
|
||||
resp.Data = fmt.Sprintf("Error in kill server handler: %s", err)
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Printf("Error encoding config file JSON reponse: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Killed Factorio server.")
|
||||
resp.Success = true
|
||||
resp.Data = fmt.Sprintf("Factorio server killed")
|
||||
} else {
|
||||
resp.Data = "Factorio server is not running"
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Printf("Error encoding config file JSON reponse: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Printf("Error encoding config file JSON reponse: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckServer(w http.ResponseWriter, r *http.Request) {
|
||||
resp := JSONResponse{
|
||||
Success: false,
|
||||
|
@ -228,6 +228,11 @@ var apiRoutes = Routes{
|
||||
"GET",
|
||||
"/server/stop",
|
||||
StopServer,
|
||||
}, {
|
||||
"KillServer",
|
||||
"GET",
|
||||
"/server/kill",
|
||||
KillServer,
|
||||
}, {
|
||||
"RunningServer",
|
||||
"GET",
|
||||
|
@ -5,6 +5,7 @@ class ServerCtl extends React.Component {
|
||||
super(props);
|
||||
this.startServer = this.startServer.bind(this);
|
||||
this.stopServer = this.stopServer.bind(this);
|
||||
this.killServer = this.killServer.bind(this);
|
||||
|
||||
this.incrementPort = this.incrementPort.bind(this);
|
||||
this.decrementPort = this.decrementPort.bind(this);
|
||||
@ -58,6 +59,21 @@ class ServerCtl extends React.Component {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
killServer(e) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/server/kill",
|
||||
dataType: "json",
|
||||
success: (resp) => {
|
||||
this.props.facServStatus();
|
||||
this.props.getStatus();
|
||||
console.log(resp)
|
||||
swal(resp.data)
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
incrementPort() {
|
||||
let port = this.state.port + 1;
|
||||
this.setState({port: port})
|
||||
@ -85,7 +101,11 @@ class ServerCtl extends React.Component {
|
||||
</div>
|
||||
|
||||
<div className="col-md-4">
|
||||
<button className="btn btn-block btn-danger" type="button" onClick={this.stopServer}><i className="fa fa-stop fa-fw"></i>Stop Factorio Server</button>
|
||||
<button className="btn btn-block btn-warning" type="button" onClick={this.stopServer}><i className="fa fa-stop fa-fw"></i>Stop & Save Factorio Server</button>
|
||||
</div>
|
||||
|
||||
<div className="col-md-4">
|
||||
<button className="btn btn-block btn-danger" type="button" onClick={this.killServer}><i className="fa fa-close fa-fw"></i>Stop Factorio Server without Saving</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -132,3 +152,4 @@ ServerCtl.propTypes = {
|
||||
}
|
||||
|
||||
export default ServerCtl
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user