From 1daf6e9610d95141eddf635ea864595ec0240a5e Mon Sep 17 00:00:00 2001 From: Vilsol Date: Wed, 8 Jun 2022 02:36:28 +0300 Subject: [PATCH] only delete owned mods, mod enable/disable, do not re-extract --- cli/installations.go | 11 ++++++++--- cli/profiles.go | 27 +++++++++++++++++++++++++++ tea/scenes/apply.go | 2 +- tea/scenes/mod.go | 18 ++++++++++++++++++ tea/scenes/mods.go | 38 +++++++++++++++++++++++++++++++------- utils/io.go | 18 +++++++++++++++++- 6 files changed, 102 insertions(+), 12 deletions(-) diff --git a/cli/installations.go b/cli/installations.go index 39ebe44..8abdece 100644 --- a/cli/installations.go +++ b/cli/installations.go @@ -329,8 +329,13 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan InstallUpdate) e for _, entry := range dir { if entry.IsDir() { if _, ok := lockfile[entry.Name()]; !ok { - if err := os.RemoveAll(filepath.Join(modsDirectory, entry.Name())); err != nil { - return errors.Wrap(err, "failed to delete mod directory") + modDir := filepath.Join(modsDirectory, entry.Name()) + _, err := os.Stat(filepath.Join(modDir, ".smm")) + if err == nil { + log.Info().Str("mod_reference", entry.Name()).Msg("deleting mod") + if err := os.RemoveAll(modDir); err != nil { + return errors.Wrap(err, "failed to delete mod directory") + } } } } @@ -384,7 +389,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan InstallUpdate) e downloading = false log.Info().Str("mod_reference", modReference).Str("version", version.Version).Str("link", version.Link).Msg("extracting mod") - if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), genericUpdates); err != nil { + if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), version.Hash, genericUpdates); err != nil { return errors.Wrap(err, "could not extract "+modReference) } diff --git a/cli/profiles.go b/cli/profiles.go index 7841dda..8285cee 100644 --- a/cli/profiles.go +++ b/cli/profiles.go @@ -296,3 +296,30 @@ func (p *Profile) Resolve(resolver DependencyResolver, lockFile *LockFile, gameV return resultLockfile, nil } + +func (p *Profile) IsModEnabled(reference string) bool { + if p.Mods == nil { + return false + } + + if mod, ok := p.Mods[reference]; ok { + return mod.Enabled + } + + return false +} + +func (p *Profile) SetModEnabled(reference string, enabled bool) { + if p.Mods == nil { + return + } + + if _, ok := p.Mods[reference]; !ok { + return + } + + p.Mods[reference] = ProfileMod{ + Version: p.Mods[reference].Version, + Enabled: enabled, + } +} diff --git a/tea/scenes/apply.go b/tea/scenes/apply.go index 39d0f76..04ac16a 100644 --- a/tea/scenes/apply.go +++ b/tea/scenes/apply.go @@ -144,7 +144,7 @@ func (m apply) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case KeyEnter: if m.status.done { if m.parent != nil { - return m.parent, nil + return m.parent, m.parent.Init() } } return m, nil diff --git a/tea/scenes/mod.go b/tea/scenes/mod.go index a212556..3dfb342 100644 --- a/tea/scenes/mod.go +++ b/tea/scenes/mod.go @@ -44,6 +44,24 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea. }, }, } + + if root.GetCurrentProfile().IsModEnabled(mod.Reference) { + items = append(items, utils.SimpleItem[modMenu]{ + ItemTitle: "Disable Mod", + Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) { + root.GetCurrentProfile().SetModEnabled(mod.Reference, false) + return currentModel.parent, currentModel.parent.Init() + }, + }) + } else { + items = append(items, utils.SimpleItem[modMenu]{ + ItemTitle: "Enable Mod", + Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) { + root.GetCurrentProfile().SetModEnabled(mod.Reference, true) + return currentModel.parent, currentModel.parent.Init() + }, + }) + } } else { items = []list.Item{ utils.SimpleItem[modMenu]{ diff --git a/tea/scenes/mods.go b/tea/scenes/mods.go index 072063f..cf6dd42 100644 --- a/tea/scenes/mods.go +++ b/tea/scenes/mods.go @@ -510,18 +510,24 @@ func (c ModsListDelegate) Render(w io.Writer, m list.Model, index int, item list } isInstalled := false + isDisabled := false if c.Context != nil { profile := c.Context.Profiles.Profiles[c.Context.Profiles.SelectedProfile] if profile != nil { if profile.HasMod(realItem.Extra.Mod_reference) { isInstalled = true + isDisabled = !profile.IsModEnabled(realItem.Extra.Mod_reference) } } } if emptyFilter { if isInstalled { - title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ " + title) + if isDisabled { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ " + title) + } else { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ " + title) + } } title = s.DimmedTitle.Render(title) } else if isSelected && m.FilterState() != list.Filtering { @@ -529,13 +535,22 @@ func (c ModsListDelegate) Render(w io.Writer, m list.Model, index int, item list unmatched := s.SelectedTitle.Inline(true) matched := unmatched.Copy().Inherit(s.FilterMatch) if isInstalled { - unmatched = unmatched.Foreground(lipgloss.Color("40")) - matched = matched.Foreground(lipgloss.Color("40")) + if isDisabled { + unmatched = unmatched.Foreground(lipgloss.Color("220")) + matched = matched.Foreground(lipgloss.Color("220")) + } else { + unmatched = unmatched.Foreground(lipgloss.Color("40")) + matched = matched.Foreground(lipgloss.Color("40")) + } } title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched) } if isInstalled { - title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title + if isDisabled { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ ") + title + } else { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title + } } title = s.SelectedTitle.Render(title) } else { @@ -543,13 +558,22 @@ func (c ModsListDelegate) Render(w io.Writer, m list.Model, index int, item list unmatched := s.NormalTitle.Inline(true) matched := unmatched.Copy().Inherit(s.FilterMatch) if isInstalled { - unmatched = unmatched.Foreground(lipgloss.Color("40")) - matched = matched.Foreground(lipgloss.Color("40")) + if isDisabled { + unmatched = unmatched.Foreground(lipgloss.Color("220")) + matched = matched.Foreground(lipgloss.Color("220")) + } else { + unmatched = unmatched.Foreground(lipgloss.Color("40")) + matched = matched.Foreground(lipgloss.Color("40")) + } } title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched) } if isInstalled { - title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title + if isDisabled { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render("✓ ") + title + } else { + title = lipgloss.NewStyle().Foreground(lipgloss.Color("40")).Render("✓ ") + title + } } title = s.NormalTitle.Render(title) } diff --git a/utils/io.go b/utils/io.go index 97472d9..c768159 100644 --- a/utils/io.go +++ b/utils/io.go @@ -140,7 +140,19 @@ func SHA256Data(f io.Reader) (string, error) { return hex.EncodeToString(h.Sum(nil)), nil } -func ExtractMod(f io.ReaderAt, size int64, location string, updates chan GenericUpdate) error { +func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates chan GenericUpdate) error { + hashFile := filepath.Join(location, ".smm") + hashBytes, err := os.ReadFile(hashFile) + if err != nil { + if !os.IsNotExist(err) { + return errors.Wrap(err, "failed to read .smm mod hash file") + } + } else { + if hash == string(hashBytes) { + return nil + } + } + if err := os.MkdirAll(location, 0777); err != nil { if !os.IsExist(err) { return errors.Wrap(err, "failed to create mod directory: "+location) @@ -181,6 +193,10 @@ func ExtractMod(f io.ReaderAt, size int64, location string, updates chan Generic } } + if err := os.WriteFile(hashFile, []byte(hash), 0777); err != nil { + return errors.Wrap(err, "failed to write .smm mod hash file") + } + if updates != nil { select { case updates <- GenericUpdate{Progress: 1}: