You've already forked factorio-server-manager
mirror of
https://github.com/OpenFactorioServerManager/factorio-server-manager.git
synced 2025-07-09 00:55:43 +02:00
238 lines
5.2 KiB
Go
238 lines
5.2 KiB
Go
package factorio
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/mroote/factorio-server-manager/lockfile"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type ModInfoList struct {
|
|
Mods []ModInfo `json:"mods"`
|
|
Destination string `json:"-"`
|
|
}
|
|
type ModInfo struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Title string `json:"title"`
|
|
Author string `json:"author"`
|
|
FileName string `json:"file_name"`
|
|
FactorioVersion Version `json:"factorio_version"`
|
|
Dependencies []string `json:"dependencies"`
|
|
Compatibility bool `json:"compatibility"`
|
|
}
|
|
|
|
func newModInfoList(destination string) (ModInfoList, error) {
|
|
var err error
|
|
modInfoList := ModInfoList{
|
|
Destination: destination,
|
|
}
|
|
|
|
err = modInfoList.listInstalledMods()
|
|
if err != nil {
|
|
log.Printf("ModInfoList ... error listing installed Mods: %s", err)
|
|
return modInfoList, err
|
|
}
|
|
|
|
return modInfoList, nil
|
|
}
|
|
|
|
func (modInfoList *ModInfoList) listInstalledMods() error {
|
|
var err error
|
|
modInfoList.Mods = nil
|
|
|
|
err = filepath.Walk(modInfoList.Destination, func(path string, info os.FileInfo, err error) error {
|
|
if !info.IsDir() && filepath.Ext(path) == ".zip" {
|
|
|
|
err = FileLock.RLock(path)
|
|
if err != nil && err == lockfile.ErrorAlreadyLocked {
|
|
log.Println(err)
|
|
return nil
|
|
} else if err != nil {
|
|
log.Printf("error locking file: %s", err)
|
|
return err
|
|
}
|
|
defer FileLock.RUnlock(path)
|
|
|
|
zipFile, err := zip.OpenReader(path)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
return err
|
|
}
|
|
defer zipFile.Close()
|
|
|
|
var modInfo ModInfo
|
|
err = modInfo.getModInfo(&zipFile.Reader)
|
|
if err != nil {
|
|
log.Fatalf("Error in getModInfo: %s", err)
|
|
}
|
|
|
|
modInfo.FileName = info.Name()
|
|
|
|
var base Version
|
|
var op string
|
|
for _, dep := range modInfo.Dependencies {
|
|
dep = strings.TrimSpace(dep)
|
|
if dep == "" {
|
|
continue
|
|
}
|
|
|
|
// skip optional and incompatible dependencies
|
|
parts := strings.Split(dep, " ")
|
|
if len(parts) > 3 {
|
|
log.Printf("skipping dependency '%s' in '%s': optional dependency or invalid format\n", dep, modInfo.Name)
|
|
continue
|
|
}
|
|
if parts[0] != "base" {
|
|
continue
|
|
}
|
|
if len(parts) == 1 {
|
|
base = modInfo.FactorioVersion
|
|
op = ">="
|
|
continue
|
|
}
|
|
|
|
op = parts[1]
|
|
|
|
if err := base.UnmarshalText([]byte(parts[2])); err != nil {
|
|
log.Printf("skipping dependency '%s' in '%s': %v\n", dep, modInfo.Name, err)
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
server := GetFactorioServer()
|
|
|
|
if !base.Equals(NilVersion) {
|
|
modInfo.Compatibility = server.Version.Compatible(base, op)
|
|
} else {
|
|
log.Println("error finding basemodDependency. Using FactorioVersion...")
|
|
modInfo.Compatibility = server.Version.GreaterC(modInfo.FactorioVersion)
|
|
}
|
|
|
|
modInfoList.Mods = append(modInfoList.Mods, modInfo)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("error while walking over the given dir: %s", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (modInfoList *ModInfoList) deleteMod(modName string) error {
|
|
var err error
|
|
|
|
//search for mod, that should be deleted
|
|
for _, mod := range modInfoList.Mods {
|
|
if mod.Name == modName {
|
|
filePath := filepath.Join(modInfoList.Destination, mod.FileName)
|
|
|
|
FileLock.LockW(filePath)
|
|
//delete mod
|
|
err = os.Remove(filePath)
|
|
FileLock.Unlock(filePath)
|
|
if err != nil {
|
|
log.Printf("ModInfoList ... error when deleting mod: %s", err)
|
|
return err
|
|
}
|
|
|
|
//reload mod-list
|
|
err = modInfoList.listInstalledMods()
|
|
if err != nil {
|
|
log.Printf("ModInfoList ... error while refreshing installedModList: %s", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
log.Printf("the mod-file for mod %s doesn't exist!", modName)
|
|
return errors.New("the mod-file for mod " + modName + " doesn't exist!")
|
|
}
|
|
|
|
func (modInfo *ModInfo) getModInfo(reader *zip.Reader) error {
|
|
for _, singleFile := range reader.File {
|
|
if singleFile.FileInfo().Name() == "info.json" {
|
|
//interpret info.json
|
|
rc, err := singleFile.Open()
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
byteArray, err := ioutil.ReadAll(rc)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return err
|
|
}
|
|
err = rc.Close()
|
|
if err != nil {
|
|
log.Printf("Error closing singleFile: %s", err)
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(byteArray, modInfo)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return errors.New("info.json not found in zip-file")
|
|
}
|
|
|
|
func (modInfoList *ModInfoList) createMod(modName string, fileName string, modFile io.Reader) error {
|
|
var err error
|
|
|
|
//save uploaded file
|
|
filePath := filepath.Join(modInfoList.Destination, fileName)
|
|
newFile, err := os.Create(filePath)
|
|
if err != nil {
|
|
log.Printf("error on creating new file - %s: %s", fileName, err)
|
|
return err
|
|
}
|
|
defer newFile.Close()
|
|
|
|
FileLock.LockW(filePath)
|
|
|
|
_, err = io.Copy(newFile, modFile)
|
|
if err != nil {
|
|
log.Printf("error on copying file to disk: %s", err)
|
|
return err
|
|
}
|
|
|
|
err = newFile.Close()
|
|
if err != nil {
|
|
log.Printf("error on closing new created zip-file: %s", err)
|
|
return err
|
|
}
|
|
|
|
FileLock.Unlock(filePath)
|
|
|
|
//reload the list
|
|
err = modInfoList.listInstalledMods()
|
|
if err != nil {
|
|
log.Printf("error on listing mod-infos: %s", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|