Skip to content

Commit

Permalink
Add dependency check when adding or updating a mod
Browse files Browse the repository at this point in the history
- Add 'members intent is disabled' message
  • Loading branch information
TechnoStrife committed Nov 22, 2020
1 parent 69bca46 commit ffe68ec
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ release/**
.env
config.json
FactoCord-3.0
FactoCord-3.0.exe
FactoCord3
FactoCord
error.log
Expand Down
108 changes: 105 additions & 3 deletions commands/admin/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/http"
"os"
"path"
"regexp"
"strings"

"github.com/maxsupermanhd/FactoCord-3.0/support"
Expand Down Expand Up @@ -121,8 +122,10 @@ type modRelease struct {
DownloadUrl string `json:"download_url"`
SHA1 string
FileName string `json:"file_name"`
Name string
Version string
InfoJson struct {
Dependencies []string
FactorioVersion string `json:"factorio_version"`
} `json:"info_json"`
}
Expand Down Expand Up @@ -304,6 +307,7 @@ func modsAdd(s *discordgo.Session, mods *ModJSON, modDescriptions *[]modDescript
toDownload = append(toDownload, release)
inserted := mods.sortedInsert(desc.ModEntry())
if inserted {
release.Name = desc.name
addedMods.Append(desc.String())
} else {
alreadyAdded.Append(desc.String())
Expand All @@ -327,6 +331,7 @@ func modsAdd(s *discordgo.Session, mods *ModJSON, modDescriptions *[]modDescript
res += alreadyAdded.RenderNotEmpty()
res += userErrors.RenderNotEmpty()
}
res += checkDependencies(toDownload, files)

if support.Config.ModPortalToken == "" {
res += "\n**No token to download mods**"
Expand Down Expand Up @@ -428,10 +433,12 @@ func modsUpdate(s *discordgo.Session, mods *ModJSON, modDescriptions *[]modDescr
for _, x := range toDownload {
downloadQueue <- x
}

dependencies := checkDependencies(toDownload, files)
if updateAll {
return updatedMods.Render() + alreadyUpdated.RenderNotEmpty() + userErrors.RenderNotEmpty()
return updatedMods.Render() + alreadyUpdated.RenderNotEmpty() + userErrors.RenderNotEmpty() + dependencies
} else {
return updatedMods.Render() + userErrors.RenderNotEmpty()
return updatedMods.Render() + userErrors.RenderNotEmpty() + dependencies
}
}

Expand Down Expand Up @@ -575,7 +582,7 @@ func removeModFiles(files *modsFilesT, modname string) (found []modDescriptionT,
}

func checkModPortal(desc *modDescriptionT, factorioVersion string) (*modRelease, string, error) {
resp, err := http.Get(fmt.Sprintf("https://mods.factorio.com/api/mods/%s", desc.name))
resp, err := http.Get(fmt.Sprintf("https://mods.factorio.com/api/mods/%s/full", desc.name))
if err != nil {
return nil, "", err
}
Expand Down Expand Up @@ -619,6 +626,101 @@ func checkModPortal(desc *modDescriptionT, factorioVersion string) (*modRelease,
}
}

var dependencyRegexp = regexp.MustCompile(`^(!|\?|\(\?\))? ?([A-Za-z0-9\-_ ]+)( ([<>]?=?) (\d+\.\d+\.\d+))?$`)

func checkDependencies(newMods []*modRelease, files *modsFilesT) string {
installed := map[string][]*support.SemanticVersionT{}
for _, mod := range newMods {
installed[mod.Name] = append(installed[mod.Name], support.SemanticVersionPanic(mod.Version))
}
for name, modFiles := range files.versions {
for _, file := range modFiles {
installed[name] = append(installed[name], &file.version)
}
}

missingModsList := support.DefaultTextList("\n**Missing dependencies:**")
incompatibleModsList := support.DefaultTextList("\n**Incompatible Mods:**")
wrongVersionMods := support.DefaultTextList("\n**Wrong version is installed:**")
var addMods []string
var updateMods []string

for _, mod := range newMods {
for _, dependency := range mod.InfoJson.Dependencies {
match := dependencyRegexp.FindStringSubmatch(dependency)
prefix := match[1]
name := strings.TrimSpace(match[2])
compare := match[4]
depVersion := match[5]
if name == "base" {
continue // TODO check factorio version
}
if prefix == "?" || prefix == "(?)" {
continue // optional dependency
}
versions, found := installed[name]
if prefix == "!" {
if found {
incompatibleModsList.Append(fmt.Sprintf("%s is incompatible with %s", mod.Name, name))
}
continue
}
if !found {
missingModsList.Append(dependency)
if compare == "" || strings.Contains(compare, ">") {
addMods = append(addMods, support.QuoteSpace(name))
} else if compare != "<" {
addMods = append(addMods, support.QuoteSpace(fmt.Sprintf("%s==%s", name, depVersion)))
}
continue
}
if compare == "" {
continue
}
matched := false
for _, modVersion := range versions {
if support.CompareOp(modVersion.Compare(support.SemanticVersionPanic(depVersion)), compare) {
matched = true
break
}
}
if !matched {
if compare == "" || strings.Contains(compare, ">") {
updateMods = append(updateMods, support.QuoteSpace(name))
} else if compare != "<" {
updateMods = append(updateMods, support.QuoteSpace(fmt.Sprintf("%s==%s", name, depVersion)))
}
versionsStr := ""
for _, version := range versions { // fucking golang
if versionsStr != "" {
versionsStr += ", "
}
versionsStr += version.Full
}
wrongVersionMods.Append(fmt.Sprintf(
"%s (%s) doesn't satisfy the dependency condition '%s %s' of %s",
name, versionsStr, compare, depVersion, mod.Name,
))
}
}
}
res := missingModsList.RenderNotEmpty() + incompatibleModsList.RenderNotEmpty() + wrongVersionMods.RenderNotEmpty()
if len(addMods) != 0 || len(updateMods) != 0 {
if len(addMods) != 0 && len(updateMods) != 0 {
res += "\nIt is recommended to run these commands:"
} else {
res += "\nIt is recommended to run this command:"
}
}
if len(addMods) != 0 {
res += support.FormatUsage("\n `$mod add " + strings.Join(addMods, " ") + "`")
}
if len(updateMods) != 0 {
res += support.FormatUsage("\n `$mod update " + strings.Join(updateMods, " ") + "`")
}
return res
}

func compareFactorioVersions(modVersion, factorioVersion string) bool {
if modVersion == "0.18" {
return factorioVersion == "0.18" || factorioVersion == "1.0"
Expand Down
7 changes: 6 additions & 1 deletion discord/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ func CacheDiscordMembers(session *discordgo.Session) (count int) {
for {
members, err := session.GuildMembers(support.GuildID, after, limit)
if err != nil {
support.Panik(err, "... when requesting members")
if resterr, ok := err.(*discordgo.RESTError); ok && resterr.Message.Code == 50001 { // Missing access
fmt.Println("You need to enable \"members intent\" for the bot " +
"if you want to use nickname colors and pings. https://i.imgur.com/PdHNJFm.png")
} else {
support.Panik(err, "... when requesting members")
}
return
}
for _, member := range members {
Expand Down
11 changes: 11 additions & 0 deletions support/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ func SemanticVersionPanic(s string) *SemanticVersionT {
return v
}

func (v *SemanticVersionT) Compare(v2 *SemanticVersionT) int {
if v.Equal(v2) {
return 0
}
if v.NewerThan(v2) {
return 1
} else {
return -1
}
}

func (v *SemanticVersionT) Equal(v2 *SemanticVersionT) bool {
return v.Full == "" || v2.Full == "" || v.Full == v2.Full
}
Expand Down
25 changes: 25 additions & 0 deletions support/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ func QuoteSplit(s string, quote string) ([]string, bool) {
return res, mismatched
}

func QuoteSpace(s string) string {
if strings.ContainsRune(s, ' ') {
s = "\"" + s + "\""
}
return s
}

func Unique(strs []string) []string {
s := make([]string, len(strs))
copy(s, strs)
Expand Down Expand Up @@ -334,3 +341,21 @@ type CommandDoc struct {
Doc string
Subcommands []CommandDoc
}

func CompareOp(cmp int, op string) bool {
switch op {
case "=", "==":
return cmp == 0
case ">":
return cmp == 1
case ">=":
return cmp >= 0
case "<":
return cmp == -1
case "<=":
return cmp <= 0
}
err := fmt.Errorf("`%s` is not a comparison operator", op)
Panik(err, "")
panic(err)
}

0 comments on commit ffe68ec

Please sign in to comment.