diff --git a/backend/app/app.go b/backend/app/app.go index 445c7de..c8d5329 100644 --- a/backend/app/app.go +++ b/backend/app/app.go @@ -53,10 +53,6 @@ func (u *Utils) WalkDirExt(root string, exts []string) ([]string, error) { return backend.WalkDirExt(root, exts) } -func (u *Utils) ReadFile(path string) (*string, error) { - return backend.ReadFile(path) -} - func (app *Application) GetSettings() *AppSettings { return app.Settings } diff --git a/backend/common/profile/manager.go b/backend/common/profile/manager.go index 2f779b2..b1eafff 100644 --- a/backend/common/profile/manager.go +++ b/backend/common/profile/manager.go @@ -1,4 +1,113 @@ package profile -type ModStore map[string]IMod -type ProfileStore = map[string]Profile +import ( + "encoding/json" + "modm8/backend" + "os" + "path/filepath" + "strings" +) + +// type ModStore map[string]IMod +// type ProfileStore = map[string]Profile + +type ProfileManager struct{} + +func NewManager() *ProfileManager { + return &ProfileManager{} +} + +type ProfileMods struct { + Thunderstore []string `json:"thunderstore"` + Nexus []string `json:"nexus"` +} + +type ProfileManifest struct { + Mods ProfileMods `json:"mods"` +} + +func (pm *ProfileManager) GetProfiles(gameTitle string) (map[string]ProfileManifest, error) { + return GetProfiles(gameTitle) +} + +func (pm *ProfileManager) GetProfile(gameTitle, profileName string) (*ProfileManifest, error) { + return GetManifest(gameTitle, profileName) +} + +func (pm *ProfileManager) SaveProfile(gameTitle, profileName string, prof ProfileManifest) error { + return SaveManifest(gameTitle, profileName, prof) +} + +func PathToProfilesDir(gameTitle string) string { + cacheDir, _ := os.UserConfigDir() + path := filepath.Join(cacheDir, "modm8", "Games", gameTitle, "Profiles") + + return path +} + +func PathToProfile(gameTitle, profileName string) string { + return filepath.Join(PathToProfilesDir(gameTitle), profileName+".prof") +} + +func GetProfileNames(gameTitle string) ([]string, error) { + profDir := PathToProfilesDir(gameTitle) + + // The user probably hasn't created a profiles yet. + exists, _ := backend.ExistsAtPath(profDir) + if !exists { + return []string{}, nil + } + + paths, err := backend.WalkDirExt(profDir, []string{"prof"}) + if err != nil { + return []string{}, err + } + + var names []string + for _, path := range paths { + name := strings.Replace(filepath.Base(path), ".prof", "", -1) + names = append(names, name) + } + + return names, nil +} + +func SaveManifest(gameTitle, profileName string, prof ProfileManifest) error { + data, err := json.Marshal(prof) + if err != nil { + return err + } + + return backend.SaveFile(PathToProfile(gameTitle, profileName), data) +} + +func GetManifest(gameTitle, profileName string) (*ProfileManifest, error) { + contents, err := backend.ReadFile(PathToProfile(gameTitle, profileName)) + if err != nil { + return nil, err + } + + var manifest ProfileManifest + if err := json.Unmarshal(contents, &manifest); err != nil { + return nil, err + } + + return &manifest, nil +} + +func GetProfiles(gameTitle string) (map[string]ProfileManifest, error) { + profNames, err := GetProfileNames(gameTitle) + if err != nil { + return nil, err + } + + profiles := make(map[string]ProfileManifest) + for _, name := range profNames { + manifest, _ := GetManifest(gameTitle, name) + if manifest != nil { + profiles[name] = *manifest + } + } + + return profiles, err +} diff --git a/backend/common/profile/profile.go b/backend/common/profile/profile.go index 870881c..ba5bc3e 100644 --- a/backend/common/profile/profile.go +++ b/backend/common/profile/profile.go @@ -1,23 +1,27 @@ package profile -type Profile struct { - Name string `json:"name" mapstructure:"name"` - Favourited bool `json:"favourited" mapstructure:"favourited"` - Mods ModStore `json:"mods" mapstructure:"mods"` -} +// type Profile struct { +// Name string `json:"name" mapstructure:"name"` +// //Favourited bool `json:"favourited" mapstructure:"favourited"` +// Mods ModStore `json:"mods" mapstructure:"mods"` +// } -func NewProfile(name string) Profile { - return Profile{ - Name: name, - Favourited: false, - Mods: ModStore{}, - } -} +// func NewProfile(name string) Profile { +// return Profile{ +// Name: name, +// //Favourited: false, +// Mods: ModStore{}, +// } +// } -func (p *Profile) Favourite() { - p.Favourited = true -} +// func (p *Profile) AddMod(name string, mod IMod) { +// p.Mods[name] = mod +// } -func (p *Profile) AddMod(name string, mod IMod) { - p.Mods[name] = mod -} +// func (p *Profile) Rename(name string) { +// p.Name = name +// } + +// // func (p *Profile) Favourite() { +// // p.Favourited = true +// // } diff --git a/backend/game/bepinex.go b/backend/game/bepinex.go index 187e268..ce3db6e 100644 --- a/backend/game/bepinex.go +++ b/backend/game/bepinex.go @@ -45,7 +45,8 @@ func ParseBepinexConfig(path string) (*BepinexConfig, error) { acceptableValues = make([]string, 0) } - lines := strings.Split(*contents, "\n") + lines := strings.Split(string(contents), "\n") + for _, line := range lines { line = strings.TrimSpace(line) diff --git a/backend/game/loader.go b/backend/game/loader.go index 3b55d6a..cee67dd 100644 --- a/backend/game/loader.go +++ b/backend/game/loader.go @@ -27,7 +27,7 @@ type LoaderPackageInfo struct { RootDirName *string } -// Note: Since Go can't do overloading, recommended version is kwargs - but only the first element will matter. +// Note: Since Go can't do overloading, recommendedVer is kwargs - but only the first element will matter. func NewLoaderPackageInfo(loaderType ModLoader, rootDirName string, recommendedVer ...*semver.Version) LoaderPackageInfo { var version *semver.Version if len(recommendedVer) > 0 { diff --git a/backend/tests/profile_test.go b/backend/tests/profile_test.go new file mode 100644 index 0000000..f813817 --- /dev/null +++ b/backend/tests/profile_test.go @@ -0,0 +1,27 @@ +package backend + +import ( + "modm8/backend/common/profile" + "testing" +) + +func TestWriteProfile(t *testing.T) { + manifest := profile.ProfileManifest{ + Mods: profile.ProfileMods{ + Thunderstore: []string{"example-ts-mod-4.2.0"}, + Nexus: []string{"example-nexus-mod-6.9.0"}, + }, + } + + err := profile.SaveManifest("Lethal Company", "test", manifest) + if err != nil { + t.Fatal(err) + } +} + +func TestReadProfile(t *testing.T) { + _, err := profile.GetManifest("Lethal Company", "test") + if err != nil { + t.Fatal(err) + } +} diff --git a/backend/thunderstore/api.go b/backend/thunderstore/api.go index 85a0115..5ac0f86 100644 --- a/backend/thunderstore/api.go +++ b/backend/thunderstore/api.go @@ -39,7 +39,7 @@ var CurModCacheDir string func ModCacheDir(gameTitle string) string { cacheDir, _ := os.UserConfigDir() - return filepath.Join(cacheDir, "modm8", "Thunderstore", gameTitle, "ModCache") + return filepath.Join(cacheDir, "modm8", "Games", gameTitle, "ModCache") } type StrippedPackage struct { diff --git a/backend/util.go b/backend/util.go index fa72b3c..57e64ee 100644 --- a/backend/util.go +++ b/backend/util.go @@ -1,7 +1,6 @@ package backend import ( - "io/fs" "os" "path/filepath" "strings" @@ -9,8 +8,8 @@ import ( func WalkDirExt(root string, exts []string) ([]string, error) { var files []string - err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if !d.IsDir() { + err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error { + if !entry.IsDir() { for _, s := range exts { if strings.HasSuffix(path, "."+s) { files = append(files, path) @@ -40,14 +39,12 @@ func ExistsAtPath(absPath string) (bool, error) { return err == nil, err } -func ReadFile(path string) (*string, error) { - content, err := os.ReadFile(filepath.Clean(path)) - if err != nil { - return nil, nil - } +func ReadFile(path string) ([]byte, error) { + return os.ReadFile(filepath.Clean(path)) +} - out := string(content) - return &out, nil +func SaveFile(path string, data []byte) error { + return os.WriteFile(filepath.Clean(path), data, os.ModePerm) } func ContainsEqualFold(arr []string, item string) bool { diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 1d829aa..375d23a 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -23,19 +23,19 @@ const { setMaxThreads } = useAppStore() const onKeydown = (event: KeyboardEvent) => { console.log('Keydown fired!\n', event.key) - const openDialogs = getOpenDialogs() - if (openDialogs.length > 0) { - for (const dialog of openDialogs) { - dialog.visible.value = false + if (event.key == "Escape") { + const openDialogs = getOpenDialogs() + if (openDialogs.length > 0) { + for (const dialog of openDialogs) { + dialog.visible.value = false + } + + return } - return + // No dialogs open + ESC -> open settings overlay. + // settingsDialog.setVisible(!settingsDialog.visible.value) } - - // No dialogs open + pressing ESC -> open settings overlay. - // if (event.key == 'Escape') { - // settingsDialog.setVisible(!settingsDialog.visible.value) - // } } onMounted(async () => { @@ -45,7 +45,6 @@ onMounted(async () => { // We don't use `navigator.hardwareConcurrency` as it is known to be unreliable. setMaxThreads(await NumCPU()) - // Add keydown event listener when the app mounts window.addEventListener('keydown', onKeydown) }) diff --git a/frontend/src/assets/styles/global.css b/frontend/src/assets/styles/global.css index 89ab324..65455a4 100644 --- a/frontend/src/assets/styles/global.css +++ b/frontend/src/assets/styles/global.css @@ -27,18 +27,19 @@ } html { - width: 100vw; - height: 100vh; + width: 100%; + height: 100%; overflow: hidden; } body { - width: 100vw; - height: 100vh; position: relative; box-sizing: border-box; margin: 0; padding: 0; + width: 100%; + height: 100%; + overflow: hidden; } body::before { diff --git a/frontend/src/components/reusable/PlatformSelectPanels.vue b/frontend/src/components/reusable/PlatformSelectPanels.vue index a7a9aea..1c0f72e 100644 --- a/frontend/src/components/reusable/PlatformSelectPanels.vue +++ b/frontend/src/components/reusable/PlatformSelectPanels.vue @@ -3,7 +3,7 @@ import Splitter from 'primevue/splitter' import SplitterPanel from 'primevue/splitterpanel' //import OutlineHoverButton from "./OutlineHoverButton.vue" -const props = defineEmits(['selectThunderstore', 'selectNexus']) +const emit = defineEmits(['selectThunderstore', 'selectNexus'])