Skip to content

Commit

Permalink
- remove Load Latest from save overviews (except under Controls)
Browse files Browse the repository at this point in the history
- add actual latest save to Load Latest
- disable Controls and Load Mods from Save when no save is present
  • Loading branch information
knoxfighter committed Jul 11, 2024
1 parent 47dcfc9 commit f78a9a4
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 41 deletions.
34 changes: 30 additions & 4 deletions src/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"sync"
"time"

Expand Down Expand Up @@ -84,23 +85,48 @@ func SaveSession(w http.ResponseWriter, r *http.Request, session *sessions.Sessi
// Lists all save files in the factorio/saves directory
func ListSaves(w http.ResponseWriter, r *http.Request) {
var resp interface{}
config := bootstrap.GetConfig()
defer func() {
WriteResponse(w, resp)
}()

w.Header().Set("Content-Type", "application/json;charset=UTF-8")

savesList, err := factorio.ListSaves(config.FactorioSavesDir)
latestParam := r.URL.Query().Get("latest")

var withLatest bool

if latestParam != "" {
var err error
withLatest, err = strconv.ParseBool(latestParam)
if err != nil {
resp = fmt.Sprintf("Error parsing latestParam: %s", err)
log.Println(resp)
w.WriteHeader(http.StatusBadRequest)
return
}
}

savesList, err := factorio.ListSaves()
if err != nil {
resp = fmt.Sprintf("Error listing save files: %s", err)
log.Println(resp)
w.WriteHeader(http.StatusInternalServerError)
return
}

loadLatest := factorio.Save{Name: "Load Latest"}
savesList = append(savesList, loadLatest)
// get actual latest and add name
// but only if requested
if withLatest && len(savesList) != 0 {
latestSave, err := factorio.GetLatestSave()
if err != nil {
resp = fmt.Sprintf("Error getting latest save: %s", err)
log.Println(resp)
w.WriteHeader(http.StatusInternalServerError)
return
}
latestSave.Name = fmt.Sprintf("Load Latest (%s)", latestSave.Name)
savesList = append(savesList, latestSave)
}

resp = savesList
}
Expand Down
6 changes: 3 additions & 3 deletions src/bootstrap/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (

type User struct {
gorm.Model
Username string `json:"username",gorm:"uniqueIndex,not null"`
Password string `json:"password",gorm:"not null"`
Role string `json:"role",gorm:"not null"`
Username string `json:"username" gorm:"uniqueIndex,not null"`
Password string `json:"password" gorm:"not null"`
Role string `json:"role" gorm:"not null"`
Email string `json:"email"`
}

Expand Down
2 changes: 1 addition & 1 deletion src/factorio/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func readString(r io.Reader, game Version, forceOptimized bool) (s string, err e
return string(d), nil
}

func (h SaveHeader) readStats(r io.Reader) (stats map[byte][]map[uint16]uint32, err error) {
func (h *SaveHeader) readStats(r io.Reader) (stats map[byte][]map[uint16]uint32, err error) {
var scratch [4]byte
stats = make(map[byte][]map[uint16]uint32)

Expand Down
31 changes: 26 additions & 5 deletions src/factorio/saves.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ type Save struct {
Size int64 `json:"size"`
}

func (s Save) String() string {
func (s *Save) String() string {
return s.Name
}

// Lists save files in factorio/saves
func ListSaves(saveDir string) (saves []Save, err error) {
func ListSaves() (saves []Save, err error) {
config := bootstrap.GetConfig()
saves = []Save{}
err = filepath.Walk(saveDir, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(config.FactorioSavesDir, func(path string, info os.FileInfo, err error) error {
if info == nil || (info.IsDir() && info.Name() == "saves") {
return nil
}
Expand All @@ -40,8 +41,7 @@ func ListSaves(saveDir string) (saves []Save, err error) {
}

func FindSave(name string) (*Save, error) {
config := bootstrap.GetConfig()
saves, err := ListSaves(config.FactorioSavesDir)
saves, err := ListSaves()
if err != nil {
return nil, fmt.Errorf("error listing saves: %v", err)
}
Expand Down Expand Up @@ -84,3 +84,24 @@ func CreateSave(filePath string) (string, error) {

return result, nil
}

func GetLatestSave() (save Save, err error) {
config := bootstrap.GetConfig()

err = filepath.Walk(config.FactorioSavesDir, func(path string, info os.FileInfo, err error) error {
if info == nil || (info.IsDir() && info.Name() == "saves") {
return nil
}

if save.LastMod.Before(info.ModTime()) {
save = Save{
Name: info.Name(),
LastMod: info.ModTime(),
Size: info.Size(),
}
}
return nil
})

return
}
8 changes: 4 additions & 4 deletions src/factorio/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (server *Server) Run() error {
ioutil.WriteFile(config.SettingsFile, data, 0644)
}

saves, err := ListSaves(config.FactorioSavesDir)
saves, err := ListSaves()
if err != nil {
log.Println("Failed to get saves list: ", err)
}
Expand Down Expand Up @@ -260,12 +260,12 @@ func (server *Server) Run() error {
args = append(args, "--server-adminlist", config.FactorioAdminFile)
}

if server.Savefile == "Load Latest" {
if strings.HasPrefix(server.Savefile, "Load Latest") {
args = append(args, "--start-server-load-latest")
} else {
args = append(args, "--start-server", filepath.Join(config.FactorioSavesDir, server.Savefile))
}

// Write chat log to a different file if requested (if not it will be mixed-in with the default logfile)
if config.ChatLogFile != "" {
args = append(args, "--console-log", config.ChatLogFile)
Expand All @@ -278,7 +278,7 @@ func (server *Server) Run() error {
log.Println("Starting server with command: ", config.FactorioBinary, args)
server.Cmd = exec.Command(config.FactorioBinary, args...)
}

server.StdOut, err = server.Cmd.StdoutPipe()
if err != nil {
log.Printf("Error opening stdout pipe: %s", err)
Expand Down
11 changes: 9 additions & 2 deletions ui/App/components/Select.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import React, {useState} from "react";
import React, {useEffect, useState} from "react";

const Select = ({register, options, className = "", defaultValue = ""}) => {
const Select = ({register, options, className = "", defaultValue = "", disabled = undefined}) => {

const [value, setValue] = useState(defaultValue);

useEffect(() => {
if (value === "") {
setValue(defaultValue)
}
});

return (
<div className={`${className} relative`}>
<select
className="shadow appearance-none border w-full py-2 px-3 text-black"
{...register}
value={value}
disabled={disabled}
onChange={optionElement => setValue(optionElement.target.value)}
>
{options.map(option => <option value={option.value} key={option.value}>{option.name}</option>)}
Expand Down
25 changes: 16 additions & 9 deletions ui/App/views/Controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ const Controls = ({serverStatus}) => {

const factorioVersion = serverStatus.fac_version ? serverStatus.fac_version : 'Unknown';
const [saves, setSaves] = useState([]);
const [isDisabled, setIsDisabled] = useState(true);
const [isStopping, setIsStopping] = useState(false);
const [isStarting, setIsStarting] = useState(false);
const [isKilling, setIsKilling] = useState(false);

const { handleSubmit, register, formState: {errors} } = useForm();
const { handleSubmit, reset, register, formState: {errors} } = useForm();

const startServer = async (data) => {
if(saves.length === 1 && saves[0].name === "Load Latest") {
window.flash("Save must be created before starting server", "red");
return;
}
setIsStarting(true);
await server.start(data.ip, parseInt(data.port), data.save);
}
Expand All @@ -38,8 +35,14 @@ const Controls = ({serverStatus}) => {
}

useEffect(() => {
savesResource.list()
.then(res => setSaves(res));
savesResource.list(true)
.then(res => {
setSaves(res);
if (res.length > 0) {
setIsDisabled(undefined);
}
reset();
});
}, [])

return (
Expand Down Expand Up @@ -80,6 +83,7 @@ const Controls = ({serverStatus}) => {
<div className="font-bold">IP</div>
<Input
defaultValue={"0.0.0.0"}
disabled={isDisabled}
register={register('ip',{required: true, pattern: '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'})}
/>
<Error error={errors.ip} message="IP is required and must be valid."/>
Expand All @@ -90,6 +94,7 @@ const Controls = ({serverStatus}) => {
type="number"
min={1}
defaultValue={"34197"}
disabled={isDisabled}
register={register('port',{required: true})}
/>
<Error error={errors.port} message="Port is required"/>
Expand All @@ -103,12 +108,14 @@ const Controls = ({serverStatus}) => {
<div className="relative">
<Select
register={register('save',{required: true})}
defaultValue="Load Latest"
defaultValue={saves.find((save) => save.name.startsWith('Load Latest'))?.name}
disabled={isDisabled}
options={saves.map(save => new Object({
value: save.name,
name: save.name
}))}
/>
<Error error={errors.save} message="Save is required and must be valid."/>
</div>
</div>
</>
Expand All @@ -122,7 +129,7 @@ const Controls = ({serverStatus}) => {
<Button onClick={stopServer} isLoading={isStopping} isDisabled={isKilling} size="sm" className="w-full md:w-auto mb-2 md:mb-0 md:mr-2" type="default">Save & Stop Server</Button>
<Button onClick={killServer} isLoading={isKilling} isDisabled={isStopping} size="sm" type="danger" className="w-full md:w-auto">Kill Server</Button>
</>
: <Button isSubmit={true} isLoading={isStarting} size="sm" type="success" className="w-full md:w-auto">Start Server</Button>
: <Button isSubmit={true} isDisabled={isDisabled} isLoading={isStarting} size="sm" type="success" className="w-full md:w-auto">Start Server</Button>
}
</div>
}
Expand Down
10 changes: 8 additions & 2 deletions ui/App/views/Mods/components/LoadMods.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ const LoadMods = ({refreshMods}) => {
const [saves, setSaves] = useState([]);
const {register, reset, handleSubmit} = useForm();
const [isLoading, setIsLoading] = useState(false);
const [isDisabled, setIsDisabled] = useState(true);

useEffect(() => {
(async () => {
setSaves(await savesResource.list());
const s = await savesResource.list()
setSaves(s);
if (s.length > 0) {
setIsDisabled(false);
}
reset();
})();
}, []);
Expand All @@ -40,12 +45,13 @@ const LoadMods = ({refreshMods}) => {
<Select
register={register('save')}
className="mb-4"
disabled={isDisabled}
options={saves?.map(save => new Object({
name: save.name,
value: save.name
}))}
/>
<Button isSubmit={true} isLoading={isLoading}>Load</Button>
<Button isSubmit={true} isDisabled={isDisabled} isLoading={isLoading}>Load</Button>
</form>
)
}
Expand Down
16 changes: 7 additions & 9 deletions ui/App/views/Saves/Saves.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,13 @@ const Saves = ({serverStatus}) => {
<td className="pr-4">{(new Date(save.last_mod)).toLocaleString()}</td>
<td className="pr-4">{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB</td>
<td>
{ save.name !== 'Load Latest' && <>
<a href={`/api/saves/dl/${save.name}`} className="mr-2">
<FontAwesomeIcon
className="text-gray-light cursor-pointer hover:text-orange"
icon={faDownload}/>
</a>
<FontAwesomeIcon className="text-red cursor-pointer hover:text-red-light mr-2"
onClick={() => deleteSave(save)} icon={faTrashAlt}/>
</>}
<a href={`/api/saves/dl/${save.name}`} className="mr-2">
<FontAwesomeIcon
className="text-gray-light cursor-pointer hover:text-orange"
icon={faDownload}/>
</a>
<FontAwesomeIcon className="text-red cursor-pointer hover:text-red-light mr-2"
onClick={() => deleteSave(save)} icon={faTrashAlt}/>
</td>
</tr>
)}
Expand Down
8 changes: 6 additions & 2 deletions ui/api/resources/saves.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import client from "../client";

export default {
list: async () => {
const response = await client.get('/api/saves/list');
list: async (latest) => {
const response = await client.get('/api/saves/list', {
params: {
latest
}
});
return response.data;
},
delete: async (save) => {
Expand Down

0 comments on commit f78a9a4

Please sign in to comment.