Implement update routines for CF

This commit is contained in:
comp500 2019-06-16 15:05:47 +01:00
parent b77e2080c7
commit b6c71191d4
No known key found for this signature in database
GPG Key ID: 214C822FFEC586B5
3 changed files with 87 additions and 12 deletions

View File

@ -8,9 +8,9 @@ type Updater interface {
// ParseUpdate takes an unparsed interface{} (as a map[string]interface{}), and returns an Updater for a mod file. // ParseUpdate takes an unparsed interface{} (as a map[string]interface{}), and returns an Updater for a mod file.
// This can be done using the mapstructure library or your own parsing methods. // This can be done using the mapstructure library or your own parsing methods.
ParseUpdate(map[string]interface{}) (interface{}, error) ParseUpdate(map[string]interface{}) (interface{}, error)
// CheckUpdate checks whether there is an update for each of the mods in the given slice, // CheckUpdate checks whether there is an update for each of the mods in the given slice, for the given MC version,
// called for all of the mods that this updater handles // called for all of the mods that this updater handles
CheckUpdate([]Mod) ([]UpdateCheck, error) CheckUpdate([]Mod, string) ([]UpdateCheck, error)
// DoUpdate carries out the update previously queried in CheckUpdate, on each Mod's metadata, // DoUpdate carries out the update previously queried in CheckUpdate, on each Mod's metadata,
// given pointers to Mods and the value of CachedState for each mod // given pointers to Mods and the value of CachedState for each mod
DoUpdate([]*Mod, []interface{}) error DoUpdate([]*Mod, []interface{}) error
@ -31,7 +31,6 @@ type UpdateCheck struct {
Error error Error error
} }
// TODO: new docs
// to carry out updating: // to carry out updating:
// go through all metafiles in index // go through all metafiles in index

View File

@ -1,6 +1,7 @@
package curseforge package curseforge
import ( import (
"errors"
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
@ -198,16 +199,91 @@ func (u cfUpdater) ParseUpdate(updateUnparsed map[string]interface{}) (interface
return updateData, err return updateData, err
} }
func (u cfUpdater) CheckUpdate(mod []core.Mod) ([]core.UpdateCheck, error) { type cachedStateStore struct {
return nil, nil modInfo
fileInfo modFileInfo
} }
func (u cfUpdater) DoUpdate(mod []*core.Mod, cachedState []interface{}) error { func (u cfUpdater) CheckUpdate(mods []core.Mod, mcVersion string) ([]core.UpdateCheck, error) {
// TODO: implement updating results := make([]core.UpdateCheck, len(mods))
// modInfoData, err := getModInfo(u.ProjectID)
// if err != nil { // TODO: make this batched
// return false, err for i, v := range mods {
// } projectRaw, ok := v.GetParsedUpdateData("curseforge")
if !ok {
results[i] = core.UpdateCheck{Error: errors.New("couldn't parse mod data")}
continue
}
project := projectRaw.(cfUpdateData)
modInfoData, err := getModInfo(project.ProjectID)
if err != nil {
results[i] = core.UpdateCheck{Error: err}
continue
}
updateAvailable := false
fileID := project.FileID
fileInfoObtained := false
var fileInfoData modFileInfo
for _, file := range modInfoData.GameVersionLatestFiles {
// TODO: change to timestamp-based comparison??
// TODO: manage alpha/beta/release correctly, check update channel?
// Choose "newest" version by largest ID
if file.GameVersion == mcVersion && file.ID > fileID {
updateAvailable = true
fileID = file.ID
}
}
if !updateAvailable {
results[i] = core.UpdateCheck{UpdateAvailable: false}
continue
}
// The API also provides some files inline, because that's efficient!
for _, file := range modInfoData.LatestFiles {
if file.ID == fileID {
fileInfoObtained = true
fileInfoData = file
}
}
if !fileInfoObtained {
fileInfoData, err = getFileInfo(project.ProjectID, fileID)
if err != nil {
results[i] = core.UpdateCheck{Error: err}
continue
}
}
results[i] = core.UpdateCheck{
UpdateAvailable: true,
UpdateString: v.FileName + " -> " + fileInfoData.FileName,
CachedState: cachedStateStore{modInfoData, fileInfoData},
}
}
return results, nil
}
func (u cfUpdater) DoUpdate(mods []*core.Mod, cachedState []interface{}) error {
// "Do" isn't really that accurate, more like "Apply", because all the work is done in CheckUpdate!
for i, v := range mods {
modState := cachedState[i].(cachedStateStore)
v.FileName = modState.fileInfo.FileName
v.Name = modState.Name
v.Download = core.ModDownload{
URL: modState.fileInfo.DownloadURL,
// TODO: murmur2 hashing may be unstable in curse api, calculate the hash manually?
// TODO: check if the hash is invalid (e.g. 0)
HashFormat: "murmur2",
Hash: strconv.Itoa(modState.fileInfo.Fingerprint),
}
v.Update["curseforge"]["ProjectID"] = modState.ID
v.Update["curseforge"]["FileID"] = modState.fileInfo.ID
}
return nil return nil
} }

View File

@ -125,7 +125,7 @@ func cmdInstall(flags core.Flags, mod string, modArgsTail []string) error {
fileInfoObtained := false fileInfoObtained := false
var fileInfoData modFileInfo var fileInfoData modFileInfo
if fileID == 0 { if fileID == 0 {
// TODO: how do we decide which version to use? // TODO: change to timestamp-based comparison??
for _, v := range modInfoData.GameVersionLatestFiles { for _, v := range modInfoData.GameVersionLatestFiles {
// Choose "newest" version by largest ID // Choose "newest" version by largest ID
if v.GameVersion == mcVersion && v.ID > fileID { if v.GameVersion == mcVersion && v.ID > fileID {