Skip to content

Commit 9addd24

Browse files
authored
Merge pull request #116 from knoxfighter/feature/install-mods-from-save
Feature/install mods from save
2 parents 7e97976 + 8bf495f commit 9addd24

File tree

7 files changed

+771
-3
lines changed

7 files changed

+771
-3
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ go build
177177

178178
* **Mitch Roote** - [roote.ca](https://roote.ca)
179179

180+
## Special Thanks
181+
- **mickael9** for reverseengineering the factorio-save-file: https://forums.factorio.com/viewtopic.php?f=5&t=8568#
182+
180183
## License
181184

182185
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details

src/mods_handler.go

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,28 @@ import (
1212
"net/http"
1313
"os"
1414
"path/filepath"
15+
"factorioSave"
16+
"time"
1517
)
1618

19+
type ModPortalStruct struct {
20+
DownloadsCount int `json:"downloads_count"`
21+
Name string `json:"name"`
22+
Owner string `json:"owner"`
23+
Releases []struct {
24+
DownloadURL string `json:"download_url"`
25+
FileName string `json:"file_name"`
26+
InfoJSON struct {
27+
FactorioVersion string `json:"factorio_version"`
28+
} `json:"info_json"`
29+
ReleasedAt time.Time `json:"released_at"`
30+
Sha1 string `json:"sha1"`
31+
Version string `json:"version"`
32+
} `json:"releases"`
33+
Summary string `json:"summary"`
34+
Title string `json:"title"`
35+
}
36+
1737
// Returns JSON response of all mods installed in factorio/mods
1838
func listInstalledModsHandler(w http.ResponseWriter, r *http.Request) {
1939
var err error
@@ -211,7 +231,7 @@ func ModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
211231
}
212232

213233
if err != nil {
214-
w.WriteHeader(500)
234+
w.WriteHeader(http.StatusInternalServerError)
215235
resp.Data = fmt.Sprintf("Error in installMod: %s", err)
216236
if err := json.NewEncoder(w).Encode(resp); err != nil {
217237
log.Printf("Error in installMod: %s", err)
@@ -227,6 +247,90 @@ func ModPortalInstallHandler(w http.ResponseWriter, r *http.Request) {
227247
}
228248
}
229249

250+
func ModPortalInstallMultipleHandler(w http.ResponseWriter, r *http.Request) {
251+
var err error
252+
resp := JSONResponse{
253+
Success: false,
254+
}
255+
256+
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
257+
r.ParseForm()
258+
259+
modsList := make([]string, 0)
260+
versionsList := make([]string, 0)
261+
262+
//Parse incoming data
263+
for key, values := range r.PostForm {
264+
if key == "mod_name" {
265+
for _, v := range values {
266+
modsList = append(modsList, v)
267+
}
268+
} else if key == "mod_version" {
269+
for _, v := range values {
270+
versionsList = append(versionsList, v)
271+
}
272+
}
273+
}
274+
275+
mods, err := newMods(config.FactorioModsDir)
276+
if err != nil {
277+
log.Printf("error creating mods: %s", err)
278+
279+
w.WriteHeader(http.StatusInternalServerError)
280+
resp.Data = fmt.Sprintf("Error in searchModPortal: %s", err)
281+
if err := json.NewEncoder(w).Encode(resp); err != nil {
282+
log.Printf("Error in searchModPortal: %s", err)
283+
}
284+
return
285+
}
286+
287+
for modIndex, mod := range modsList {
288+
var err error
289+
290+
//get details of mod
291+
modDetails, err, statusCode := getModDetails(mod)
292+
if err != nil {
293+
w.WriteHeader(statusCode)
294+
resp.Data = fmt.Sprintf("Error in searchModPortal: %s", err)
295+
if err := json.NewEncoder(w).Encode(resp); err != nil {
296+
log.Printf("Error in searchModPortal: %s", err)
297+
}
298+
return
299+
}
300+
301+
modDetailsArray := []byte(modDetails)
302+
var modDetailsStruct ModPortalStruct
303+
304+
//read mod-data into Struct
305+
err = json.Unmarshal(modDetailsArray, &modDetailsStruct)
306+
if err != nil {
307+
log.Printf("error reading modPortalDetails: %s", err)
308+
309+
w.WriteHeader(http.StatusInternalServerError)
310+
resp.Data = fmt.Sprintf("Error in searchModPortal: %s", err)
311+
if err := json.NewEncoder(w).Encode(resp); err != nil {
312+
log.Printf("Error in searchModPortal: %s", err)
313+
}
314+
return
315+
}
316+
317+
//find correct mod-version
318+
for _, release := range modDetailsStruct.Releases {
319+
if release.Version == versionsList[modIndex] {
320+
mods.downloadMod(release.DownloadURL, release.FileName, modDetailsStruct.Name)
321+
break
322+
}
323+
}
324+
}
325+
326+
resp.Data = mods.listInstalledMods()
327+
328+
resp.Success = true
329+
if err := json.NewEncoder(w).Encode(resp); err != nil {
330+
log.Printf("Error in ToggleModHandler: %s", err)
331+
}
332+
}
333+
230334
func ToggleModHandler(w http.ResponseWriter, r *http.Request) {
231335
var err error
232336
resp := JSONResponse{
@@ -244,7 +348,7 @@ func ToggleModHandler(w http.ResponseWriter, r *http.Request) {
244348
}
245349

246350
if err != nil {
247-
w.WriteHeader(500)
351+
w.WriteHeader(http.StatusInternalServerError)
248352
resp.Data = fmt.Sprintf("Error in listInstalledModsByFolder: %s", err)
249353
if err := json.NewEncoder(w).Encode(resp); err != nil {
250354
log.Printf("Error in listInstalledModsByFolder: %s", err)
@@ -276,7 +380,7 @@ func DeleteModHandler(w http.ResponseWriter, r *http.Request) {
276380
}
277381

278382
if err != nil {
279-
w.WriteHeader(500)
383+
w.WriteHeader(http.StatusInternalServerError)
280384
resp.Data = fmt.Sprintf("Error in deleteMod: %s", err)
281385
if err := json.NewEncoder(w).Encode(resp); err != nil {
282386
log.Printf("Error in DeleteModHandler: %s", err)
@@ -455,6 +559,45 @@ func DownloadModsHandler(w http.ResponseWriter, r *http.Request) {
455559
writerHeader.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", "all_installed_mods.zip"))
456560
}
457561

562+
//Returns JSON response with the found mods
563+
func LoadModsFromSaveHandler(w http.ResponseWriter, r *http.Request) {
564+
var err error
565+
resp := JSONResponse{
566+
Success: false,
567+
}
568+
569+
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
570+
571+
//Get Data out of the request
572+
SaveFile := r.FormValue("saveFile")
573+
574+
SaveFileComplete := filepath.Join(config.FactorioSavesDir, SaveFile)
575+
resp.Data, err = factorioSave.ReadHeader(SaveFileComplete)
576+
577+
if err == factorioSave.ErrorIncompatible {
578+
w.WriteHeader(http.StatusInternalServerError)
579+
resp.Data = fmt.Sprintf("%s<br>Only can read 0.16.x save files", err)
580+
if err := json.NewEncoder(w).Encode(resp); err != nil {
581+
log.Printf("Error in loadModsFromSave: %s", err)
582+
}
583+
return
584+
}
585+
if err != nil {
586+
w.WriteHeader(http.StatusInternalServerError)
587+
resp.Data = fmt.Sprintf("Error in searchModPortal: %s", err)
588+
if err := json.NewEncoder(w).Encode(resp); err != nil {
589+
log.Printf("Error in loadModsFromSave: %s", err)
590+
}
591+
return
592+
}
593+
594+
resp.Success = true
595+
596+
if err := json.NewEncoder(w).Encode(resp); err != nil {
597+
log.Printf("Error in LoadModsFromSave: %s", err)
598+
}
599+
}
600+
458601
func ListModPacksHandler(w http.ResponseWriter, r *http.Request) {
459602
var err error
460603
resp := JSONResponse{

src/routes.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ var apiRoutes = Routes{
183183
"POST",
184184
"/mods/install",
185185
ModPortalInstallHandler,
186+
}, {
187+
"ModPortalInstallMultiple",
188+
"POST",
189+
"/mods/install/multiple",
190+
ModPortalInstallMultipleHandler,
186191
}, {
187192
"ToggleMod",
188193
"POST",
@@ -213,6 +218,11 @@ var apiRoutes = Routes{
213218
"GET",
214219
"/mods/download",
215220
DownloadModsHandler,
221+
}, {
222+
"LoadModsFromSave",
223+
"POST",
224+
"/mods/save/load",
225+
LoadModsFromSaveHandler,
216226
}, {
217227
"ListSaves",
218228
"GET",

0 commit comments

Comments
 (0)