Compare commits
16 Commits
3406ab4743
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 283ffb66eb | |||
| 3d24198ad3 | |||
| 9e7bd21602 | |||
| e051d2d6f6 | |||
| 6ef8f280a8 | |||
| cf71d956d2 | |||
| bb65f409b4 | |||
| 8fb2c2aeb2 | |||
| aecc2162e7 | |||
| 2548261d66 | |||
| b2f485269b | |||
| aef97fe613 | |||
| bdf636b0d3 | |||
| e7f967c6bd | |||
| 86ccf302ea | |||
| b2c534f29b |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.vscode
|
||||
factorio-headless_linux_2.0.72
|
||||
factorio
|
||||
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# FactoryMan
|
||||
|
||||
A simple Factorio server manager for linux systems.
|
||||
|
||||
* Download and install headless factorio server (requires api key)
|
||||
* Download and install mods directly from mod-list.json
|
||||
* Start and stop factorio in a screen session, great for remote servers
|
||||
* Backup saves and server
|
||||
|
||||
### Commands
|
||||
|
||||
```shell
|
||||
$> ./factoryman download server
|
||||
```
|
||||
|
||||
Run this command to download factorio-headless_linux_latest.
|
||||
|
||||
It will install to path in serverFolder in ``config.yml``
|
||||
|
||||
*NOTE: username and apitoken are required in ``config.yml``>factoryman*
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
$> ./factoryman download mod
|
||||
```
|
||||
|
||||
Run this to download mods directly from ``$serverFolder/mods/mod-list.json``
|
||||
|
||||
*NOTE: username and apitoken are required in ``config.yml``>factoryman*
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
$> ./factoryman start
|
||||
```
|
||||
|
||||
Start factorio server (in screen session by default)
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
$> ./factoryman stop
|
||||
```
|
||||
|
||||
Stop factorio server (in screen session)
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
$> ./factoryman backup saves
|
||||
```
|
||||
|
||||
Backup saves to path in ``config.yml``>factoryman>backupDir
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
$> ./factoryman backup full
|
||||
```
|
||||
|
||||
Backup Full serverDir to backupDir
|
||||
|
||||
---
|
||||
|
||||
*Note:*
|
||||
|
||||
use ``$> screen -LS`` to view server terminal
|
||||
|
||||
### Simple Config
|
||||
|
||||
```config.yml```
|
||||
|
||||
```yaml
|
||||
server:
|
||||
serverFolder: "factorio"
|
||||
worldFile: ""
|
||||
serverSettings: "factorio/data/server-settings.json"
|
||||
serverExec: "factorio/bin/x64/factorio"
|
||||
port: 34197
|
||||
|
||||
factoryman:
|
||||
screen: True
|
||||
screenName: "Factorio"
|
||||
backupDir: "factorio/backups"
|
||||
username: ""
|
||||
apitoken: ""
|
||||
```
|
||||
|
||||
Default config assumes you have used factoryman to download the server
|
||||
|
||||
36
backup.go
Normal file
36
backup.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func backUp(cmd string, c UsrConfig) {
|
||||
switch cmd {
|
||||
case "full":
|
||||
fmt.Println("Starting full server backup")
|
||||
t := time.Now()
|
||||
timeStamp := t.Format(time.RFC3339)
|
||||
fullBackupName := fmt.Sprintf("%s/ServerBackup.%s.tgz", c.Factoryman.BackupDir, timeStamp)
|
||||
fullBackup := exec.Command("tar", "-czvf", fullBackupName, c.Server.ServDir)
|
||||
fullBackup.Stdout = os.Stdout
|
||||
err := fullBackup.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("Backup Failed: %s\n", err)
|
||||
}
|
||||
case "saves":
|
||||
fmt.Println("Backing up saves")
|
||||
t := time.Now()
|
||||
timeStamp := t.Format(time.RFC3339)
|
||||
savesBackupName := fmt.Sprintf("%s/SaveBackup.%s.tgz", c.Factoryman.BackupDir, timeStamp)
|
||||
saveDir := fmt.Sprintf("%s/saves", c.Server.ServDir)
|
||||
savesBackup := exec.Command("tar", "-czvf", savesBackupName, saveDir)
|
||||
savesBackup.Stdout = os.Stdout
|
||||
err := savesBackup.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("Backup Failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
43
config.go
Normal file
43
config.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func DefCfg() *DefConfig {
|
||||
c := &DefConfig{}
|
||||
|
||||
// server settings
|
||||
c.Server.ServDir = "factorio"
|
||||
c.Server.ServPort = 34197
|
||||
c.Server.ServCfg = "factorio/data/server-settings.json"
|
||||
c.Server.ServExec = "factorio/bin/x64/factorio"
|
||||
c.Server.WorldFile = "factorio/saves/newworld.zip"
|
||||
|
||||
// factoryman settings
|
||||
c.Factoryman.UseScreen = true
|
||||
c.Factoryman.ScreenName = "factorio"
|
||||
c.Factoryman.BackupDir = "factorio/backups"
|
||||
c.Factoryman.UserName = ""
|
||||
c.Factoryman.ApiToken = ""
|
||||
return c
|
||||
}
|
||||
func readCfg(factCfg string) UsrConfig {
|
||||
//read config file (YAML)
|
||||
fileBytes, err := os.ReadFile(factCfg)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading config.yml file, using defaults\n")
|
||||
return UsrConfig(*DefCfg())
|
||||
}
|
||||
// return Struct
|
||||
var config UsrConfig
|
||||
err = yaml.Unmarshal(fileBytes, &config)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshalling YAML file: %v", err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
10
config.yml
10
config.yml
@@ -1,9 +1,13 @@
|
||||
server:
|
||||
serverFolder: "factorio"
|
||||
port: 34197
|
||||
worldFile: "factorio/newworld.zip"
|
||||
worldFile: "factorio/saves/newworld.zip"
|
||||
serverSettings: "factorio/data/server-settings.json"
|
||||
backupDir: "backups"
|
||||
serverExec: "factorio/bin/x64/factorio"
|
||||
port: 34197
|
||||
|
||||
factoryman:
|
||||
screen: True
|
||||
screenName: "Factorio"
|
||||
backupDir: "factorio/backups"
|
||||
username: ""
|
||||
apitoken: ""
|
||||
BIN
factoryman
BIN
factoryman
Binary file not shown.
14
go.mod
14
go.mod
@@ -1,5 +1,15 @@
|
||||
module gitlab.com/Raum0x2A/factoryman
|
||||
|
||||
go 1.22.2
|
||||
go 1.25.7
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1
|
||||
require (
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/therootcompany/xz v1.0.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
19
go.sum
19
go.sum
@@ -1,4 +1,21 @@
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
42
launchserver.go
Normal file
42
launchserver.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func startStopServer(cmd string, con UsrConfig) {
|
||||
switch cmd {
|
||||
case "start":
|
||||
x := fmt.Sprintf("%s --port %d --server-settings %s --start-server %s", con.Server.ServExec, con.Server.ServPort, con.Server.ServCfg, con.Server.WorldFile)
|
||||
if con.Factoryman.UseScreen { // if screen enabled in config.yml
|
||||
fmt.Println("Starting factorio server in screen session")
|
||||
startScreenCmd := exec.Command("screen", "-dmS", con.Factoryman.ScreenName, "bash", "-c", x, "; exec sh")
|
||||
startScreenCmd.Stdout = os.Stdout
|
||||
startScreenCmd.Stderr = os.Stderr
|
||||
err := startScreenCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server: %s", err)
|
||||
}
|
||||
} else {
|
||||
startSrvCmd := exec.Command("bash", "-c", x)
|
||||
startSrvCmd.Stdout = os.Stdout
|
||||
startSrvCmd.Stderr = os.Stdout
|
||||
startSrvCmd.Stdin = os.Stdin
|
||||
err := startSrvCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server: %s", err)
|
||||
}
|
||||
}
|
||||
case "stop":
|
||||
quitServerCmd := exec.Command("screen", "-S", con.Factoryman.ScreenName, "-p", "0", "-X", "stuff", "/quit\n")
|
||||
err := quitServerCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Command failed: %s, Error: %v", quitServerCmd.Args, err)
|
||||
} else {
|
||||
fmt.Println("Server Stopped")
|
||||
}
|
||||
}
|
||||
}
|
||||
195
main.go
195
main.go
@@ -2,142 +2,97 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const fmConfig = "config.yml"
|
||||
|
||||
type GoConfig struct {
|
||||
Config struct {
|
||||
ServDir string `yaml:"serverFolder"`
|
||||
ServPort int `yaml:"port"`
|
||||
WorldFile string `yaml:"worldFile"`
|
||||
ServCfg string `yaml:"serverSettings"`
|
||||
ServExec string `yaml:"serverExec"`
|
||||
BackupDir string `yaml:"backupDir"`
|
||||
UseScreen bool `yaml:"screen"`
|
||||
ScreenName string `yaml:"screenName"`
|
||||
} `yaml:"server"`
|
||||
}
|
||||
|
||||
func readCfg(factCfg string) GoConfig {
|
||||
//read config file (YAML)
|
||||
fileBytes, err := os.ReadFile(factCfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading config file: %v", err)
|
||||
}
|
||||
// return Struct
|
||||
var config GoConfig
|
||||
err = yaml.Unmarshal(fileBytes, &config)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshalling YAML file: %v", err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func startStopServer(cmd string, con GoConfig) {
|
||||
switch cmd {
|
||||
case "start":
|
||||
x := fmt.Sprintf("%s --port %d --server-settings %s --start-server %s", con.Config.ServExec, con.Config.ServPort, con.Config.ServCfg, con.Config.WorldFile)
|
||||
|
||||
//x := fmt.Sprintf("%s --port %d --server-settings %s --start-server %s; exec sh", con.Config.ServExec, con.Config.ServPort, con.Config.ServCfg, con.Config.WorldFile)
|
||||
///home/raum/factorio-serverfmt.Println(x)
|
||||
if con.Config.UseScreen {
|
||||
fmt.Println("Starting factorio server in screen session")
|
||||
startScreenCmd := exec.Command("screen", "-dmS", con.Config.ScreenName, "bash", "-c", x, "; exec sh")
|
||||
err := startScreenCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server: %s", err)
|
||||
} else {
|
||||
fmt.Printf("Started server on port %d, in screen named %s\n", con.Config.ServPort, con.Config.ScreenName)
|
||||
}
|
||||
} else {
|
||||
startSrvCmd := exec.Command(x)
|
||||
err := startSrvCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server: %s", err)
|
||||
}
|
||||
}
|
||||
case "stop":
|
||||
quitServerCmd := exec.Command("screen", "-S", con.Config.ScreenName, "-p", "0", "-X", "stuff", "/quit\n")
|
||||
err := quitServerCmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Command failed: %s, Error: %v", quitServerCmd.Args, err)
|
||||
} else {
|
||||
fmt.Printf("Server in screen %s stopped\n", con.Config.ScreenName)
|
||||
}
|
||||
fmt.Println("Waiting for server to shutdown\r")
|
||||
//time.Sleep(20 * time.Second)
|
||||
//fmt.Println("Closing screen session ", con.Config.ScreenName)
|
||||
//stopScreenCmd := exec.Command("screen", "-S", con.Config.ScreenName, "-p", "0", "-X", "stuff", "exit\n")
|
||||
//err = stopScreenCmd.Run()
|
||||
//if err != nil {
|
||||
// log.Fatalf("Command failed: %s, Error: %v", stopScreenCmd.Args, err)
|
||||
//} else {
|
||||
// fmt.Printf("Screen \"%s\" closed\n", con.Config.ScreenName)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
func backUp(cmd string, c GoConfig) {
|
||||
switch cmd {
|
||||
case "full":
|
||||
fmt.Println("Starting full server backup")
|
||||
t := time.Now()
|
||||
timeStamp := t.Format(time.RFC3339)
|
||||
fullBackupName := fmt.Sprintf("%s/ServerBackup.%s.tgz", c.Config.BackupDir, timeStamp)
|
||||
fullBackup := exec.Command("tar", "-czf", fullBackupName, c.Config.ServDir)
|
||||
err := fullBackup.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("Backup Failed: %s\n", err)
|
||||
}
|
||||
case "saves":
|
||||
fmt.Println("Backing up saves")
|
||||
t := time.Now()
|
||||
timeStamp := t.Format(time.RFC3339)
|
||||
savesBackupName := fmt.Sprintf("%s/SaveBackup.%s.tgz", c.Config.BackupDir, timeStamp)
|
||||
saveDir := fmt.Sprintf("%s/saves", c.Config.ServDir)
|
||||
savesBackup := exec.Command("tar", "-czf", savesBackupName, saveDir)
|
||||
err := savesBackup.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("Backup Failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := readCfg(fmConfig)
|
||||
var c = readCfg("config.yml")
|
||||
|
||||
//start command config flags
|
||||
startCmd := flag.NewFlagSet("start", flag.ExitOnError)
|
||||
startScreen := startCmd.BoolP("screen", "s", c.Factoryman.UseScreen, "Start server in screen session")
|
||||
startServerExec := startCmd.StringP("exec", "e", c.Server.ServExec, "Path to server executable")
|
||||
startSettings := startCmd.StringP("config", "c", c.Server.ServCfg, "Server config json")
|
||||
startPort := startCmd.IntP("port", "p", c.Server.ServPort, "Port to start server on")
|
||||
startScrName := startCmd.StringP("name", "n", c.Factoryman.ScreenName, "Name for screen session")
|
||||
startWorld := startCmd.StringP("world", "w", c.Server.WorldFile, "World file")
|
||||
|
||||
// stop command config flags
|
||||
stopCmd := flag.NewFlagSet("stop", flag.ExitOnError)
|
||||
stopScrName := stopCmd.StringP("name", "n", c.Factoryman.ScreenName, "Screen session name")
|
||||
|
||||
// download command flags
|
||||
downloadCmd := flag.NewFlagSet("download", flag.ExitOnError)
|
||||
downloadSrvLoc := downloadCmd.StringP("path", "d", c.Server.ServDir, "Server Directory path")
|
||||
downloadAPIKey := downloadCmd.StringP("token", "t", c.Factoryman.ApiToken, "API token")
|
||||
downloadUserName := downloadCmd.StringP("username", "u", c.Factoryman.UserName, "Factorio username")
|
||||
|
||||
// backup command flags
|
||||
backupCmd := flag.NewFlagSet("backup", flag.ExitOnError)
|
||||
backupDir := backupCmd.StringP("path", "d", c.Factoryman.BackupDir, "Path to backup directory")
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Expected subcommand. \n\tex.) start, stop, download")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "start":
|
||||
fmt.Println("starting server")
|
||||
|
||||
startCmd.Parse(os.Args[2:])
|
||||
//map user input settings to config struct
|
||||
c.Factoryman.UseScreen = *startScreen
|
||||
c.Server.ServExec = *startServerExec
|
||||
c.Server.ServCfg = *startSettings
|
||||
c.Server.ServPort = *startPort
|
||||
c.Factoryman.ScreenName = *startScrName
|
||||
c.Server.WorldFile = *startWorld
|
||||
|
||||
startStopServer("start", c)
|
||||
case "stop":
|
||||
fmt.Println("stopping server")
|
||||
|
||||
stopCmd.Parse(os.Args[2:])
|
||||
c.Factoryman.ScreenName = *stopScrName
|
||||
|
||||
startStopServer("stop", c)
|
||||
case "backup":
|
||||
if len(os.Args) > 2 {
|
||||
case "download":
|
||||
downloadCmd.Parse(os.Args[2:])
|
||||
c.Server.ServDir = *downloadSrvLoc
|
||||
c.Factoryman.ApiToken = *downloadAPIKey
|
||||
c.Factoryman.UserName = *downloadUserName
|
||||
|
||||
switch os.Args[2] {
|
||||
case "mods":
|
||||
fmt.Println("Processing mod-list.json")
|
||||
downloadMods(c)
|
||||
case "server":
|
||||
fmt.Println("starting download")
|
||||
downloadHeadless(c)
|
||||
default:
|
||||
fmt.Println("mods or server")
|
||||
os.Exit(1)
|
||||
}
|
||||
case "backup":
|
||||
backupCmd.Parse(os.Args[2:])
|
||||
c.Factoryman.BackupDir = *backupDir
|
||||
|
||||
switch os.Args[2] {
|
||||
case "full":
|
||||
backUp("full", c)
|
||||
case "saves":
|
||||
backUp("saves", c)
|
||||
case "full":
|
||||
backUp("full", c)
|
||||
default:
|
||||
fmt.Println("Invalid backup type: use 'full' or 'saves'")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Missing backup type: use 'full' or 'saves'")
|
||||
fmt.Println("saves or full")
|
||||
os.Exit(1)
|
||||
}
|
||||
case "dev":
|
||||
fmt.Println("=)")
|
||||
default:
|
||||
fmt.Printf("Unknown command: %s. Use 'start', 'stop', or 'backup'.\n", os.Args[1])
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Use 'start', 'stop', or 'backup' command")
|
||||
fmt.Println("ex. factoryman start")
|
||||
fmt.Println("To configure edit 'conifg.yml'")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
84
mods.go
Normal file
84
mods.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func downloadMods(c UsrConfig) {
|
||||
//var c = readCfg("config.yml")
|
||||
modlist := string(c.Server.ServDir) + "/mods/mod-list.json"
|
||||
|
||||
fmt.Printf("Updating mods from %s\n", modlist)
|
||||
|
||||
//read from modlist
|
||||
file, err := os.Open(modlist)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading modlist: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// create temp directory
|
||||
tempDir, err := os.MkdirTemp("", "factoryman-*")
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to create temporary directory")
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
//decode json response from api
|
||||
var modList ModList
|
||||
decoder := json.NewDecoder(file)
|
||||
if err := decoder.Decode(&modList); err != nil {
|
||||
log.Fatalf("Error reading JSON: %v", err)
|
||||
}
|
||||
// Iterate over modlist.json
|
||||
for _, mod := range modList.Mods {
|
||||
switch mod.Name {
|
||||
case "base":
|
||||
fmt.Println("Skipping base...")
|
||||
case "elevated-rails":
|
||||
fmt.Println("Skipping elevated-rails...")
|
||||
case "quality":
|
||||
fmt.Println("Skipping quality...")
|
||||
case "space-age":
|
||||
fmt.Println("Skipping space-age...")
|
||||
default:
|
||||
// query mod on modportal api
|
||||
modportalurl := fmt.Sprintf("https://mods.factorio.com/api/mods/%s", mod.Name)
|
||||
resp, err := http.Get(modportalurl)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading JSON: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// decode json response
|
||||
var moddata ModPortal
|
||||
if err := json.NewDecoder(resp.Body).Decode(&moddata); err != nil {
|
||||
log.Fatalf("Error reading JSON: %v", err)
|
||||
}
|
||||
|
||||
// query download url for mod
|
||||
accessToken := fmt.Sprintf("?username=%s&token=%s", c.Factoryman.UserName, c.Factoryman.ApiToken)
|
||||
modDownloadUrl := fmt.Sprintf("https://mods.factorio.com%s%s", moddata.Releases[len(moddata.Releases)-1].DownloadUrl, accessToken)
|
||||
fileName := string(tempDir + "/" + moddata.Releases[len(moddata.Releases)-1].Filename)
|
||||
//download mod archive
|
||||
downloadErr := download(fileName, modDownloadUrl)
|
||||
if downloadErr != nil {
|
||||
log.Fatalf("Error downloading: %v", downloadErr)
|
||||
}
|
||||
fmt.Printf("Downloaded: %s\n", fileName)
|
||||
fmt.Printf("extracting files to %s\n", c.Server.ServDir+"/mods/")
|
||||
// extract archive to mods folder
|
||||
//exec.Command("unzip", fileName, "-d", c.Server.ServDir+"/mods/")
|
||||
_, extracterr := unzip(fileName, c.Server.ServDir+"/mods/")
|
||||
if extracterr != nil {
|
||||
log.Fatalf("Error extracting archive: %v", extracterr)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
49
serv-install.go
Normal file
49
serv-install.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/therootcompany/xz"
|
||||
)
|
||||
|
||||
func downloadHeadless(c UsrConfig) {
|
||||
//var c = readCfg("config.yml")
|
||||
headlessQuery := "https://www.factorio.com/get-download/latest/headless/linux64"
|
||||
|
||||
url := headlessQuery
|
||||
resp, err := http.Get(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
log.Fatal("Download failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Wrap stream in XZ and Tar readers
|
||||
xzReader, _ := xz.NewReader(resp.Body, 0)
|
||||
tr := tar.NewReader(xzReader)
|
||||
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
target := filepath.Join(".", header.Name)
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
os.MkdirAll(target, 0755)
|
||||
case tar.TypeReg:
|
||||
os.MkdirAll(filepath.Dir(target), 0755)
|
||||
outFile, _ := os.Create(target)
|
||||
io.Copy(outFile, tr)
|
||||
outFile.Close()
|
||||
}
|
||||
}
|
||||
// zip does not preserve permissions correctly so i have to do this
|
||||
exec.Command("chmod", "+x", c.Server.ServExec)
|
||||
}
|
||||
76
structs.go
Normal file
76
structs.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
// config struct
|
||||
type UsrConfig struct {
|
||||
Server struct { // server specific settings
|
||||
ServDir string `yaml:"serverFolder"`
|
||||
WorldFile string `yaml:"worldFile"`
|
||||
ServCfg string `yaml:"serverSettings"`
|
||||
ServExec string `yaml:"serverExec"`
|
||||
ServPort int `yaml:"port"`
|
||||
} `yaml:"server"`
|
||||
|
||||
Factoryman struct { // factoryman settings
|
||||
BackupDir string `yaml:"backupDir"`
|
||||
UseScreen bool `yaml:"screen"`
|
||||
ScreenName string `yaml:"screenName"`
|
||||
UserName string `yaml:"username"`
|
||||
ApiToken string `yaml:"apitoken"`
|
||||
} `yaml:"factoryman"`
|
||||
}
|
||||
|
||||
type DefConfig struct {
|
||||
Server struct { // server specific settings
|
||||
ServDir string
|
||||
WorldFile string
|
||||
ServCfg string
|
||||
ServExec string
|
||||
ServPort int
|
||||
}
|
||||
|
||||
Factoryman struct { // factoryman settings
|
||||
BackupDir string
|
||||
UseScreen bool
|
||||
ScreenName string
|
||||
UserName string
|
||||
ApiToken string
|
||||
}
|
||||
}
|
||||
|
||||
// JSON file from config
|
||||
type Mod struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type ModList struct {
|
||||
Mods []Mod `json:"mods"`
|
||||
}
|
||||
|
||||
// JSON response from mod poartal api
|
||||
type ModPortal struct {
|
||||
Category string `json:"category"`
|
||||
DownloadCount int `json:"downloads_count"`
|
||||
Name string `json:"name"`
|
||||
Owner string `json:"owner"`
|
||||
Releases []Release `json:"releases"`
|
||||
Score float64 `json:"score"`
|
||||
Summary string `json:"summary"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
DownloadUrl string `json:"download_url"`
|
||||
Filename string `json:"file_name"`
|
||||
InfoJson InfoJSON `json:"info_json"`
|
||||
ReleaseTime string `json:"released_at"`
|
||||
Sha1 string `json:"sha1"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type InfoJSON struct {
|
||||
Dependencies []string `json:"dependencies"`
|
||||
FactorioVersion string `json:"factorio_version"`
|
||||
}
|
||||
93
utils.go
Normal file
93
utils.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//Utilities
|
||||
|
||||
func download(filedest string, url string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
out, err := os.Create(filedest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func unzip(src, dest string) ([]string, error) {
|
||||
|
||||
//store file names available in a array of strings
|
||||
var filenames []string
|
||||
|
||||
r, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
|
||||
defer r.Close()
|
||||
|
||||
for _, f := range r.File {
|
||||
// Skip folders to prevent permission issues
|
||||
if strings.Contains(f.Name, "__MACOSX") {
|
||||
continue
|
||||
}
|
||||
|
||||
fpath := filepath.Join(dest, f.Name)
|
||||
|
||||
// Checking for any invalid file paths
|
||||
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||
return filenames, fmt.Errorf("%s is an illegal filepath", fpath)
|
||||
}
|
||||
|
||||
filenames = append(filenames, fpath)
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(fpath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
// Creating the files in the target directory
|
||||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath,
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
||||
f.Mode())
|
||||
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
}
|
||||
|
||||
return filenames, nil
|
||||
}
|
||||
Reference in New Issue
Block a user