package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "strings" "time" _ "time/tzdata" // embed the IANA tz database so America/Los_Angeles resolves // even in debian:12-slim, which ships no /usr/share/zoneinfo. ) // resolveRegionDir finds the active world's Region dir under a 7DTD saves root // (the dir mounted as the saves volume — holds serverconfig.xml + .local/...). // Prefers serverconfig GameWorld/GameName when that world has real data // (main.ttw); else falls back to the most-recently-written Saves/*/ // with data — handles RWG seed-named worlds. func resolveRegionDir(savesRoot string) (string, error) { raw, err := os.ReadFile(filepath.Join(savesRoot, "serverconfig.xml")) if err != nil { return "", fmt.Errorf("read serverconfig.xml: %w", err) } gw := xmlProp(string(raw), "GameWorld") gn := xmlProp(string(raw), "GameName") base := filepath.Join(savesRoot, ".local", "share", "7DaysToDie", "Saves") hasWorld := func(d string) bool { if _, e := os.Stat(filepath.Join(d, "main.ttw")); e == nil { return true } fi, e := os.Stat(filepath.Join(d, "Region")) return e == nil && fi.IsDir() } if gw != "" && gn != "" { lit := filepath.Join(base, gw, gn) if hasWorld(lit) { return filepath.Join(lit, "Region"), nil } } if gn != "" { // RWG fallback: newest world dir with this GameName. worlds, _ := os.ReadDir(base) var newest string var newestT time.Time for _, w := range worlds { if !w.IsDir() { continue } cand := filepath.Join(base, w.Name(), gn) if !hasWorld(cand) { continue } if fi, e := os.Stat(cand); e == nil && fi.ModTime().After(newestT) { newestT, newest = fi.ModTime(), cand } } if newest != "" { return filepath.Join(newest, "Region"), nil } } return "", fmt.Errorf("could not resolve active Region dir (GameWorld=%q GameName=%q)", gw, gn) } // xmlProp pulls the value from (tolerant of // attribute order + quote style; skips comments). func xmlProp(content, name string) string { for _, raw := range strings.Split(content, "\n") { line := strings.TrimSpace(raw) if line == "" || strings.HasPrefix(line, "