package main import ( "encoding/json" "fmt" "io" "net/http" "os" "os/exec" "strings" "time" "git.skdevstudios.com/specCon18/reforgerds-updater/internal/a2s" ) type Update struct { Version string `json:"version"` Build string `json:"build"` Published string `json:"published"` } const ( updateURL = "http://127.0.0.1:3000/updates" stateFilePath = "latest_version.txt" serverIP = "127.0.0.1" serverPort = 17777 ) func main() { resp, err := http.Get(updateURL) if err != nil { fmt.Printf("Failed to fetch updates: %v\n", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var updates []Update err = json.Unmarshal(body, &updates) if err != nil { fmt.Printf("Failed to parse response: %v\n", err) return } if len(updates) == 0 { fmt.Println("No updates found.") return } latest := updates[0].Version prevVersion := "" if data, err := os.ReadFile(stateFilePath); err == nil { prevVersion = strings.TrimSpace(string(data)) } if prevVersion == "" { fmt.Println("No previous version found. Storing latest:", latest) _ = os.WriteFile(stateFilePath, []byte(latest), 0644) return } if versionCompare(latest, prevVersion) > 0 { fmt.Printf("New version found! %s > %s\n", latest, prevVersion) // Always update the state file err := os.WriteFile(stateFilePath, []byte(latest), 0644) if err != nil { fmt.Printf("Failed to write version file: %v\n", err) } // Check for online players players, err := fetchPlayers(serverIP, serverPort) if err != nil { fmt.Printf("Error checking players: %v\n", err) return } if len(players) > 0 { fmt.Printf("Players are currently online (%d):\n", len(players)) for _, p := range players { fmt.Printf("- %-16s | Score: %d | Time: %s\n", p.Name, p.Score, formatDuration(p.Duration)) } fmt.Println("Skipping update while players are online.") return } // No players — run steamcmd fmt.Println("No players online. Running update command...") cmd := exec.Command("./steamcmd.sh", "+runscript", "reforger_update") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { fmt.Printf("Update command failed: %v\n", err) return } } else { fmt.Printf("No new version. Latest seen: %s\n", prevVersion) } } func versionCompare(a, b string) int { segA := strings.Split(a, ".") segB := strings.Split(b, ".") max := len(segA) if len(segB) > max { max = len(segB) } for i := 0; i < max; i++ { var ai, bi int if i < len(segA) { fmt.Sscanf(segA[i], "%d", &ai) } if i < len(segB) { fmt.Sscanf(segB[i], "%d", &bi) } if ai > bi { return 1 } if ai < bi { return -1 } } return 0 } func fetchPlayers(ip string, port int) ([]a2s.Player, error) { client, err := a2s.New(ip, port) if err != nil { return nil, fmt.Errorf("create client: %w", err) } defer client.Close() client.SetBufferSize(2048) client.SetDeadlineTimeout(3) players, err := client.GetPlayers() if err != nil { return nil, fmt.Errorf("get players: %w", err) } return *players, nil } func formatDuration(d time.Duration) string { minutes := int(d.Minutes()) seconds := int(d.Seconds()) % 60 return fmt.Sprintf("%02d:%02d", minutes, seconds) }