diff --git a/.gitignore b/.gitignore index 0daf6e8..2dcde26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode -factorio-headless_linux_2.0.72 \ No newline at end of file +factorio-headless_linux_2.0.72 +factorio \ No newline at end of file diff --git a/backup.go b/backup.go index 797b9d0..7d4ea89 100644 --- a/backup.go +++ b/backup.go @@ -13,8 +13,8 @@ func backUp(cmd string, c GoConfig) { 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", "-czvf", fullBackupName, c.Config.ServDir) + 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 { @@ -24,8 +24,8 @@ func backUp(cmd string, c GoConfig) { 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) + 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() diff --git a/cli.go b/cli.go new file mode 100644 index 0000000..8098850 --- /dev/null +++ b/cli.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "os" +) + +func cliToolMode() { + var c = readCfg("config.yml") + modlist := string(c.Server.ServDir) + "/mods/mod-list.json" + if len(os.Args) > 1 { + switch os.Args[1] { + case "start": + if verifyConfig(c) { + startStopServer("start", c) + } + case "stop": + if verifyConfig(c) { + startStopServer("stop", c) + } + case "help", "h", "--help", "-h": + fmt.Printf("Start Server: %s start\nStop Server: %s stop\n", os.Args[0], os.Args[0]) + fmt.Printf("Run backup\n\tFull backup: %s backup full", os.Args[0]) + fmt.Printf("\n\tBackup saves: %s backup saves\n", os.Args[0]) + case "download": + if len(os.Args) > 2 { + switch os.Args[2] { + case "mods": + if !findmodlist(modlist) { + fmt.Printf("FAILED TO FIND MOD LIST") + } else { + fmt.Printf("found %s\n", modlist) + downloadMods(modlist) + } + case "server": + downloadHeadless() + default: + fmt.Println("Invalid mod option: use 'update'") + } + } else { + fmt.Println("Missing mod option: use 'update'") + } + case "backup": + if len(os.Args) > 2 { + switch os.Args[2] { + case "full": + backUp("full", c) + case "saves": + backUp("saves", c) + default: + fmt.Println("Invalid backup type: use 'full' or 'saves'") + } + } else { + fmt.Println("Missing backup type: use 'full' or 'saves'") + } + default: + fmt.Printf("Unknown command: %s. Use 'start', 'stop', or 'backup'.\n", os.Args[1]) + } + } else { + fmt.Println("Use 'start', 'stop', or 'backup' command\nex. factoryman start\nTo configure edit 'config.yml'") + } +} diff --git a/config.go b/config.go index aa0b415..f142876 100644 --- a/config.go +++ b/config.go @@ -1,27 +1,14 @@ package main import ( + "errors" + "fmt" "log" "os" "gopkg.in/yaml.v3" ) -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) @@ -36,3 +23,40 @@ func readCfg(factCfg string) GoConfig { } return config } +func isItReal(path string) bool { //check if path exists + if _, err := os.Stat(path); err == nil { + return true + } else if errors.Is(err, os.ErrNotExist) { + return false + } else { + return false + } +} +func verifyConfig(config GoConfig) bool { // check each file/dir to see if it exists + if !isItReal(config.Server.ServDir) { //check for Server directory + fmt.Printf("PATH NOT FOUND: %s", config.Server.ServDir) + return false + } + if !isItReal(config.Server.WorldFile) { // check for world file + fmt.Printf("PATH NOT FOUND: %s", config.Server.WorldFile) + return false + } + if !isItReal(config.Server.ServCfg) { // check for server config + fmt.Printf("PATH NOT FOUND: %s", config.Server.ServCfg) + return false + } + if !isItReal(config.Server.ServExec) { // check for server exec file to lanch server + fmt.Printf("PATH NOT FOUND: %s", config.Server.ServExec) + return false + } + if !isItReal(config.Factoryman.BackupDir) { // check for backup directory + fmt.Printf("PATH NOT FOUND: %s", config.Factoryman.BackupDir) + return false + } + + if config.Factoryman.ApiToken == "" || config.Factoryman.UserName == "" { + fmt.Printf("API Token/Username not set (to download mods add api token and username to config)") + } + return true + +} diff --git a/config.yml b/config.yml index a0fae0d..82215c5 100644 --- a/config.yml +++ b/config.yml @@ -1,9 +1,13 @@ server: - serverFolder: "factorio-headless_linux_2.0.72/factorio" + serverFolder: "factorio" + worldFile: "factorio/saves/test.zip" + serverSettings: "factorio/data/server-settings.json" + serverExec: "factorio/bin/x64/factorio" port: 34197 - worldFile: "factorio-headless_linux_2.0.72/factorio/Newworlds.zip" - serverSettings: "factorio-headless_linux_2.0.72/factorio/data/server-settings.json" - backupDir: "factorio-headless_linux_2.0.72/backups" - serverExec: "factorio-headless_linux_2.0.72/factorio/bin/x64/factorio" + +factoryman: screen: True screenName: "Factorio" + backupDir: "factorio/backups" + username: "" + apitoken: "" \ No newline at end of file diff --git a/factoryman b/factoryman index 9f1d808..fe45087 100755 Binary files a/factoryman and b/factoryman differ diff --git a/go.mod b/go.mod index e2ef12e..293c40f 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,38 @@ module gitlab.com/Raum0x2A/factoryman -go 1.24.0 +go 1.25.7 -toolchain go1.24.4 +require ( + golift.io/xtractr v0.3.1 + gopkg.in/yaml.v3 v3.0.1 +) -require gopkg.in/yaml.v3 v3.0.1 +require ( + github.com/Unpackerr/iso9660 v0.0.3 // indirect + github.com/andybalholm/brotli v1.2.0 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.6.1 // indirect + github.com/bodgit/windows v1.0.1 // indirect + github.com/cavaliergopher/cpio v1.0.1 // indirect + github.com/cavaliergopher/rpm v1.3.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/icza/bitio v1.1.0 // indirect + github.com/klauspost/compress v1.18.4 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mewkiz/flac v1.0.13 // indirect + github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d // indirect + github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985 // indirect + github.com/nwaples/rardecode/v2 v2.2.2 // indirect + github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 // indirect + github.com/pierrec/lz4/v4 v4.1.26 // indirect + github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de // indirect + github.com/therootcompany/xz v1.0.1 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + go4.org v0.0.0-20260112195520-a5071408f32f // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/text v0.34.0 // indirect + golift.io/udf v0.0.1 // indirect +) diff --git a/go.sum b/go.sum index a62c313..99ebec5 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,92 @@ -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +github.com/Unpackerr/iso9660 v0.0.3 h1:WXXFIcmDLhnsKhXjPg2moUmHxhoUmIX7FLxrtqHJ7yQ= +github.com/Unpackerr/iso9660 v0.0.3/go.mod h1:4Py6ZWQ+sUVo4BmmzZaFgOLcS3to5BMvH39TlOYNxhA= +github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= +github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= +github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= +github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= +github.com/cavaliergopher/rpm v1.3.0 h1:UHX46sasX8MesUXXQ+UbkFLUX4eUWTlEcX8jcnRBIgI= +github.com/cavaliergopher/rpm v1.3.0/go.mod h1:vEumo1vvtrHM1Ov86f6+k8j7zNKOxQfHDCAIcR/36ZI= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= +github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= +github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mewkiz/flac v1.0.13 h1:6wF8rRQKBFW159Daqx6Ro7K5ZnlVhHUKfS5aTsC4oXs= +github.com/mewkiz/flac v1.0.13/go.mod h1:HfPYDA+oxjyuqMu2V+cyKcxF51KM6incpw5eZXmfA6k= +github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d h1:IL2tii4jXLdhCeQN69HNzYYW1kl0meSG0wt5+sLwszU= +github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d/go.mod h1:SIpumAnUWSy0q9RzKD3pyH3g1t5vdawUAPcW5tQrUtI= +github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985 h1:h8O1byDZ1uk6RUXMhj1QJU3VXFKXHDZxr4TXRPGeBa8= +github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985/go.mod h1:uiPmbdUbdt1NkGApKl7htQjZ8S7XaGUAVulJUJ9v6q4= +github.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU= +github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= +github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 h1:27L3dHkYbeWGU3/5NasAzVDgXG9QzlfKCvcl4cdNW6c= +github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8/go.mod h1:hpFkyhCgB5Rm8FK+ISypOE+9UyrCuL6MNcjPMB1s1ec= +github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= +github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de h1:uIeuAon/xwRdiZaCmEd5mocquesYkWCf71WBO7obTmA= +github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de/go.mod h1:XIUpD+1rteMazWrMFjNSpM6TocSHxDYXk6UEgBb5+F0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= +github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw= +go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golift.io/udf v0.0.1 h1:kEcJVzqqR+IEWGMuPjuVPT9DzXRDukEgsizKAKn1LF8= +golift.io/udf v0.0.1/go.mod h1:ndK7AlWOh+u+nW9tNsQR95dfHsfASG5Y3dMyzVqmPjw= +golift.io/xtractr v0.3.1 h1:91ZU0DBL3SSfrVYbrENSLUQG4BdHT91rTqkod7TfOUI= +golift.io/xtractr v0.3.1/go.mod h1:X3IQQIiYgwDmAuqeRSBfQqvo6KIWrFzCADTx3yFmt7o= 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/launchserver.go b/launchserver.go index d944b0f..7ecef79 100644 --- a/launchserver.go +++ b/launchserver.go @@ -10,10 +10,10 @@ import ( 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) - if con.Config.UseScreen { // if screen enabled in confing.yml + 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.Config.ScreenName, "bash", "-c", x, "; exec sh") + startScreenCmd := exec.Command("screen", "-dmS", con.Factoryman.ScreenName, "bash", "-c", x, "; exec sh") startScreenCmd.Stdout = os.Stdout startScreenCmd.Stderr = os.Stderr err := startScreenCmd.Run() @@ -31,7 +31,7 @@ func startStopServer(cmd string, con GoConfig) { } } case "stop": - quitServerCmd := exec.Command("screen", "-S", con.Config.ScreenName, "-p", "0", "-X", "stuff", "/quit\n") + 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) diff --git a/main.go b/main.go index 8b50ea8..dfa0d85 100644 --- a/main.go +++ b/main.go @@ -5,42 +5,10 @@ import ( "os" ) -var c = readCfg(fmConfig) - func main() { if len(os.Args) == 1 { - fmt.Println("Use 'start', 'stop', or 'backup' command") - fmt.Println("ex. factoryman start") - fmt.Println("To configure edit 'conifg.yml'") - } else if len(os.Args) > 1 { - switch os.Args[1] { - case "start": - startStopServer("start", c) - case "stop": - startStopServer("stop", c) - case "help", "h", "--help", "-h": - fmt.Printf("Start Server: %s start\nStop Server: %s stop\n", os.Args[0], os.Args[0]) - fmt.Printf("Run backup\n\tFull backup: %s backup full", os.Args[0]) - fmt.Printf("\n\tBackup saves: %s backup saves\n", os.Args[0]) - case "backup": - if len(os.Args) > 2 { - switch os.Args[2] { - case "full": - backUp("full", c) - case "saves": - backUp("saves", c) - default: - fmt.Println("Invalid backup type: use 'full' or 'saves'") - } - } else { - fmt.Println("Missing backup type: use 'full' or 'saves'") - } - default: - fmt.Printf("Unknown command: %s. Use 'start', 'stop', or 'backup'.\n", os.Args[1]) - } + fmt.Println("Use 'start', 'stop', or 'backup' command\nex. factoryman start\nTo configure edit 'config.yml'") } else { - fmt.Println("Use 'start', 'stop', or 'backup' command") - fmt.Println("ex. factoryman start") - fmt.Println("To configure edit 'conifg.yml'") + cliToolMode() } } diff --git a/mods.go b/mods.go new file mode 100644 index 0000000..3d7706e --- /dev/null +++ b/mods.go @@ -0,0 +1,91 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" +) + +func findmodlist(modlist string) bool { + if !isItReal(modlist) { + return false + } else { + return true + } +} + +func downloadMods(modlist string) { + var c = readCfg("config.yml") + + 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) + } + } + + } + +} diff --git a/serv-install.go b/serv-install.go new file mode 100644 index 0000000..f73cf1e --- /dev/null +++ b/serv-install.go @@ -0,0 +1,45 @@ +package main + +import ( + "archive/tar" + "io" + "log" + "net/http" + "os" + "path/filepath" + + "github.com/therootcompany/xz" +) + +func downloadHeadless() { + 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() + } + } +} diff --git a/structs.go b/structs.go new file mode 100644 index 0000000..e2fbc3c --- /dev/null +++ b/structs.go @@ -0,0 +1,58 @@ +package main + +// config struct +type GoConfig 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"` +} + +// 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"` +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..3207273 --- /dev/null +++ b/utils.go @@ -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 +}