From 26cc812e73b35397cc2d2c328edca92c9ada995a Mon Sep 17 00:00:00 2001 From: MrFastwind Date: Mon, 6 Jul 2026 16:10:24 +0200 Subject: [PATCH] feat(backend): add mod manager service for scanning workshop/local mods --- backend/internal/services/mod_manager.go | 120 +++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 backend/internal/services/mod_manager.go diff --git a/backend/internal/services/mod_manager.go b/backend/internal/services/mod_manager.go new file mode 100644 index 0000000..e61da97 --- /dev/null +++ b/backend/internal/services/mod_manager.go @@ -0,0 +1,120 @@ +package services + +import ( + "os" + "path/filepath" +) + +type ModInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Source string `json:"source"` + Path string `json:"path"` + Size int64 `json:"size"` + InUse bool `json:"in_use"` + Modlists []string `json:"modlists"` +} + +func ListWorkshopMods(serverfileDir string) []ModInfo { + workshopDir := filepath.Join(serverfileDir, "steamapps", "workshop", "content", workshopAppID) + entries, err := os.ReadDir(workshopDir) + if err != nil { + return nil + } + + var mods []ModInfo + for _, e := range entries { + if !e.IsDir() { + continue + } + id := e.Name() + p := filepath.Join(workshopDir, id) + size := dirSize(p) + mods = append(mods, ModInfo{ + ID: id, + Name: "", + Source: "workshop", + Path: p, + Size: size, + }) + } + return mods +} + +func ListLocalMods(modsDir string) []ModInfo { + entries, err := os.ReadDir(modsDir) + if err != nil { + return nil + } + + var mods []ModInfo + for _, e := range entries { + if !e.IsDir() { + continue + } + name := e.Name() + p := filepath.Join(modsDir, name) + size := dirSize(p) + displayName := name + if len(name) > 0 && name[0] == '@' { + displayName = name[1:] + } + mods = append(mods, ModInfo{ + ID: displayName, + Name: name, + Source: "local", + Path: p, + Size: size, + }) + } + return mods +} + +func BuildUsageMap(modlists *ModlistManager) (usedWorkshop map[string][]string, usedLocal map[string][]string) { + usedWorkshop = make(map[string][]string) + usedLocal = make(map[string][]string) + + lists, err := modlists.List() + if err != nil { + return + } + + for _, li := range lists { + ml, err := modlists.Get(li.ID) + if err != nil { + continue + } + for _, m := range ml.Mods { + if m.ID != "" { + usedWorkshop[m.ID] = append(usedWorkshop[m.ID], ml.Name) + } + if m.Name != "" { + nameWithAt := "@" + m.Name + usedLocal[m.Name] = append(usedLocal[m.Name], ml.Name) + usedLocal[nameWithAt] = append(usedLocal[nameWithAt], ml.Name) + } + } + } + return +} + +func RemoveMod(path string) error { + return os.RemoveAll(path) +} + +func dirSize(path string) int64 { + var size int64 + filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error { + if err != nil { + return nil + } + if !d.IsDir() { + fi, err := d.Info() + if err == nil { + size += fi.Size() + } + } + return nil + }) + return size +}