// Package config загружает и сохраняет настройки самого менеджера. package config import ( "fmt" "os" "path/filepath" "time" "gopkg.in/yaml.v3" ) // Config — всё, что менеджеру нужно знать о хосте и о сервере PZ. type Config struct { // Listen — адрес веб-панели. Для доступа только через SSH-туннель // используйте 127.0.0.1:8080. Listen string `yaml:"listen"` // DataDir — рабочая директория менеджера (пользователи, сессии, бэкапы). DataDir string `yaml:"data_dir"` // ServerDir — куда SteamCMD ставит серверные файлы PZ. ServerDir string `yaml:"server_dir"` // ZomboidDir — ~/Zomboid: сохранения, конфиги, логи. ZomboidDir string `yaml:"zomboid_dir"` // SteamCMDPath — путь к бинарю steamcmd.sh (пустой = ищем в PATH). SteamCMDPath string `yaml:"steamcmd_path"` // ServerName — имя набора конфигов PZ (servertest -> servertest.ini). ServerName string `yaml:"server_name"` // AdminPassword — пароль админа игрового сервера, нужен при первом // запуске PZ. Хранится здесь, чтобы не вводить его в консоли. AdminPassword string `yaml:"admin_password"` // JavaMemory — -Xmx для JVM сервера, например "8g". JavaMemory string `yaml:"java_memory"` // ExtraArgs — дополнительные аргументы запуска сервера. ExtraArgs []string `yaml:"extra_args"` // AutoStart — поднимать игровой сервер сразу при старте менеджера. AutoStart bool `yaml:"autostart"` // AutoRestart — перезапускать сервер, если он упал сам. AutoRestart bool `yaml:"autorestart"` // StopTimeout — сколько ждать корректного завершения после "quit". StopTimeout Duration `yaml:"stop_timeout"` // LogBufferLines — сколько строк консоли держать в памяти для веб-панели. LogBufferLines int `yaml:"log_buffer_lines"` // Backup — настройки резервного копирования. Backup BackupConfig `yaml:"backup"` path string } // BackupConfig описывает расписание и ротацию бэкапов мира. type BackupConfig struct { // Dir — куда складывать архивы. Dir string `yaml:"dir"` // Schedule — интервал автобэкапа; 0 отключает автоматику. Schedule Duration `yaml:"schedule"` // Keep — сколько последних архивов хранить (0 = не удалять). Keep int `yaml:"keep"` // StopServer — останавливать сервер на время бэкапа. Безопаснее, но // игроков выкидывает; по умолчанию выключено. StopServer bool `yaml:"stop_server"` } // Default возвращает конфиг с разумными значениями для Ubuntu. func Default() *Config { home, _ := os.UserHomeDir() return &Config{ Listen: "127.0.0.1:8080", DataDir: filepath.Join(home, ".local", "share", "pzmanager"), ServerDir: filepath.Join(home, "pzserver"), ZomboidDir: filepath.Join(home, "Zomboid"), ServerName: "servertest", JavaMemory: "4g", AutoRestart: true, StopTimeout: Duration(90 * time.Second), LogBufferLines: 5000, Backup: BackupConfig{ Dir: filepath.Join(home, "pzbackups"), Schedule: Duration(6 * time.Hour), Keep: 20, }, } } // Load читает конфиг с диска, дополняя пропущенные поля значениями по // умолчанию. Если файла нет — создаёт его. func Load(path string) (*Config, error) { cfg := Default() cfg.path = path raw, err := os.ReadFile(path) if os.IsNotExist(err) { if err := cfg.Save(); err != nil { return nil, fmt.Errorf("создать конфиг %s: %w", path, err) } return cfg, nil } if err != nil { return nil, fmt.Errorf("прочитать конфиг %s: %w", path, err) } if err := yaml.Unmarshal(raw, cfg); err != nil { return nil, fmt.Errorf("разобрать конфиг %s: %w", path, err) } cfg.applyFallbacks() return cfg, nil } // applyFallbacks подставляет умолчания вместо пустых значений: конфиг, // написанный руками, редко заполнен целиком. func (c *Config) applyFallbacks() { d := Default() if c.Listen == "" { c.Listen = d.Listen } if c.DataDir == "" { c.DataDir = d.DataDir } if c.ServerDir == "" { c.ServerDir = d.ServerDir } if c.ZomboidDir == "" { c.ZomboidDir = d.ZomboidDir } if c.ServerName == "" { c.ServerName = d.ServerName } if c.JavaMemory == "" { c.JavaMemory = d.JavaMemory } if c.StopTimeout <= 0 { c.StopTimeout = d.StopTimeout } if c.LogBufferLines <= 0 { c.LogBufferLines = d.LogBufferLines } if c.Backup.Dir == "" { c.Backup.Dir = d.Backup.Dir } } // Save записывает конфиг атомарно: сначала во временный файл рядом, затем // переименование, чтобы падение посреди записи не оставило огрызок. func (c *Config) Save() error { if c.path == "" { return fmt.Errorf("путь к конфигу неизвестен") } if err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil { return err } raw, err := yaml.Marshal(c) if err != nil { return err } tmp := c.path + ".tmp" if err := os.WriteFile(tmp, raw, 0o600); err != nil { return err } return os.Rename(tmp, c.path) } // Path возвращает путь, откуда конфиг был загружен. func (c *Config) Path() string { return c.path } // SetPath задаёт файл для последующего Save. func (c *Config) SetPath(p string) { c.path = p } // EnsureDirs создаёт директории, без которых менеджер не сможет работать. func (c *Config) EnsureDirs() error { for _, dir := range []string{c.DataDir, c.ServerDir, c.Backup.Dir} { if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("создать %s: %w", dir, err) } } return nil } // ServerConfigPath — путь к servertest.ini (или другому имени сервера). func (c *Config) ServerConfigPath() string { return filepath.Join(c.ZomboidDir, "Server", c.ServerName+".ini") } // SandboxConfigPath — путь к _SandboxVars.lua. func (c *Config) SandboxConfigPath() string { return filepath.Join(c.ZomboidDir, "Server", c.ServerName+"_SandboxVars.lua") } // SavesDir — директория с мирами (то, что бэкапим). func (c *Config) SavesDir() string { return filepath.Join(c.ZomboidDir, "Saves") }