package services import ( "context" "fmt" "os" "os/exec" "path/filepath" "sync" "time" ) func CheckWorkshopMod(serverfileDir, modID string) bool { if modID == "" { return false } p := filepath.Join(serverfileDir, "steamapps", "workshop", "content", "107410", modID) fi, err := os.Stat(p) return err == nil && fi.IsDir() } const arma3AppID = "233780" type SteamCmdManager struct { serverfileDir string streamer *LogStreamer mu sync.Mutex cancel context.CancelFunc running bool } func NewSteamCmdManager(serverfileDir string, streamer *LogStreamer) *SteamCmdManager { return &SteamCmdManager{ serverfileDir: serverfileDir, streamer: streamer, } } func (s *SteamCmdManager) IsRunning() bool { s.mu.Lock() defer s.mu.Unlock() return s.running } func (s *SteamCmdManager) UpdateGame(branch, user string) error { s.mu.Lock() if s.running { s.mu.Unlock() return fmt.Errorf("steamcmd already running") } s.mu.Unlock() args := []string{ "+force_install_dir", s.serverfileDir, "+login", user, "+app_update", arma3AppID, } if branch != "" && branch != "stable" { args = append(args, "-beta", branch) } args = append(args, "validate", "+quit") return s.run("Game update", args) } func (s *SteamCmdManager) DownloadMod(modID string) error { return s.DownloadMods([]string{modID}) } func (s *SteamCmdManager) DownloadMods(modIDs []string) error { s.mu.Lock() if s.running { s.mu.Unlock() return fmt.Errorf("steamcmd already running") } s.mu.Unlock() if len(modIDs) == 0 { return fmt.Errorf("no mod ids provided") } args := []string{ "+force_install_dir", s.serverfileDir, "+login", "anonymous", } for _, id := range modIDs { args = append(args, "+workshop_download_item", "107410", id) } args = append(args, "+quit") label := fmt.Sprintf("Download %d mod(s)", len(modIDs)) return s.run(label, args) } const steamcmdTimeout = 10 * time.Minute func (s *SteamCmdManager) run(label string, args []string) error { ctx, cancel := context.WithTimeout(context.Background(), steamcmdTimeout) s.streamer.Broadcast("steamcmd", "[STEAMCMD] "+label+" starting...") cmd := exec.CommandContext(ctx, "steamcmd", args...) stdout, err := cmd.StdoutPipe() if err != nil { cancel() return fmt.Errorf("stdout pipe: %w", err) } stderr, err := cmd.StderrPipe() if err != nil { cancel() return fmt.Errorf("stderr pipe: %w", err) } if err := cmd.Start(); err != nil { cancel() return fmt.Errorf("start steamcmd: %w", err) } s.mu.Lock() s.running = true s.cancel = cancel s.mu.Unlock() go s.streamer.Stream("steamcmd", stdout, "") go s.streamer.Stream("steamcmd", stderr, "") go func() { err := cmd.Wait() s.mu.Lock() s.running = false s.cancel = nil s.mu.Unlock() if err == nil { s.streamer.Broadcast("steamcmd", "[STEAMCMD] SUCCESS: "+label+" finished") } else { s.streamer.Broadcast("steamcmd", "[STEAMCMD] ERROR: "+label+" failed: "+err.Error()) } }() return nil }