diff --git a/frontend/src/components/ServerList.tsx b/frontend/src/components/ServerList.tsx index 963bf2c..9417edc 100644 --- a/frontend/src/components/ServerList.tsx +++ b/frontend/src/components/ServerList.tsx @@ -1,6 +1,6 @@ import { Button, Card, List, ListItem, ListItemButton } from "@mui/joy"; import { server } from "../../wailsjs/go/models"; -import { LogError } from "../../wailsjs/runtime/runtime"; +import { LogError } from "../../wailsjs/runtime"; import { IconPlus } from "@tabler/icons-react"; enum ServerListType { diff --git a/frontend/src/pages/InstallUpdater.tsx b/frontend/src/pages/InstallUpdater.tsx index d9c74ce..32edd25 100644 --- a/frontend/src/pages/InstallUpdater.tsx +++ b/frontend/src/pages/InstallUpdater.tsx @@ -56,7 +56,7 @@ export function InstallUpdater({setServ, serv, onInstalled}: Props) { useEffect(() => { EventsOn("installingUpdateAction", (data) => {setAction(data);}) EventsOn("installingUpdateProgress", (data) => {setProgress(data);}) - EventsOn("appInstalled", (i) => {setIsCompleted(true); setAction("Done"); setProgress(100)}) + EventsOn("appInstalled", () => {setIsCompleted(true); setAction("Done"); setProgress(100)}) }, []); diff --git a/frontend/src/pages/Server.tsx b/frontend/src/pages/Server.tsx index e37b443..2bfeecc 100644 --- a/frontend/src/pages/Server.tsx +++ b/frontend/src/pages/Server.tsx @@ -1,7 +1,7 @@ import { Button, ButtonGroup, - Card, Checkbox, DialogActions, + Card, DialogActions, DialogContent, DialogTitle, Divider, IconButton, diff --git a/frontend/src/pages/server/Administration.tsx b/frontend/src/pages/server/Administration.tsx index c0bd828..ab7c1b6 100644 --- a/frontend/src/pages/server/Administration.tsx +++ b/frontend/src/pages/server/Administration.tsx @@ -11,11 +11,10 @@ import { Typography } from "@mui/joy"; import React, {useState} from "react"; -import {DeleteProfile, DeleteServerFiles} from "../../../wailsjs/go/server/ServerController"; +import {DeleteProfile, DeleteServerFiles, GetServerStartupCommand} from "../../../wailsjs/go/server/ServerController"; import {server} from "../../../wailsjs/go/models"; import {useAlert} from "../../components/AlertProvider"; import {IconAlertCircleFilled, IconInfoCircle} from "@tabler/icons-react"; -import {GetServerStartupCommand} from "../../../wailsjs/go/server/ServerController"; type Props = { setServ: React.Dispatch> @@ -24,13 +23,11 @@ type Props = { } -export function Administration({setServ, serv, onServerFilesDeleted}: Props) { +function ServerAdministrationCard({setServ, serv, onServerFilesDeleted}: {setServ: React.Dispatch>, serv: server.Server, onServerFilesDeleted: () => void}) { const [deleteServerFilesModalOpen, setDeleteServerFilesModalOpen] = useState(false) const [deleteProfileModalOpen, setDeleteProfileModalOpen] = useState(false) const [deleteEverythingModalOpen, setDeleteEverythingModalOpen] = useState(false) - const [showServerCommandModalOpen, setShowServerCommandModalOpen] = useState(false) - const [serverCommand, setServerCommand] = useState(""); const {addAlert} = useAlert(); @@ -46,167 +43,221 @@ export function Administration({setServ, serv, onServerFilesDeleted}: Props) { DeleteServerFiles(serv.id).then(() => DeleteProfile(serv.id)).then(() => {addAlert("Deleted everything", "success"); location.reload()}).catch((err) => {console.error(err); addAlert(err, "danger")}) } + + + return + + Server Administration + + + +
+
+ setDeleteServerFilesModalOpen(false)}> + + + + Confirmation + + + + Are you sure you want to delete the server files? You cannot reverse this action! + + + + + + + + +
+
+ setDeleteProfileModalOpen(false)}> + + + + Confirmation + + + + Are you sure you want to delete the profile? You cannot reverse this action! + + + + + + + + +
+
+ setDeleteEverythingModalOpen(false)}> + + + + Confirmation + + + + Are you sure you want to delete everything? You cannot reverse this action! + + + + + + + + +
+
+
; +} +function ServerStartupCard({setServ, serv}: {setServ: React.Dispatch>, serv: server.Server}) { + + const [showServerCommandModalOpen, setShowServerCommandModalOpen] = useState(false) + const [serverCommand, setServerCommand] = useState(""); + + const {addAlert} = useAlert(); + return ( - - + +
- Server Administration + Server startup - - -
-
- setDeleteServerFilesModalOpen(false)}> - - - - Confirmation - - - - Are you sure you want to delete the server files? You cannot reverse this action! - - - - - - - - -
-
- setDeleteProfileModalOpen(false)}> - - - - Confirmation - - - - Are you sure you want to delete the profile? You cannot reverse this action! - - - - - - - - -
-
- setDeleteEverythingModalOpen(false)}> - - - - Confirmation - - - - Are you sure you want to delete everything? You cannot reverse this action! - - - - - - - - -
-
- - -
- - Server startup - - -
-
- setShowServerCommandModalOpen(false)}> - - - - Server startup command - - - - {serverCommand} - - - - - - - - + + + + + -
+ }}>Show startup command
-
-
- +
+ + + -
-
- setServ((p) => ({ ...p, disableUpdateOnStart: e.target.checked, convertValues: p.convertValues }))} />
- {/* setServ((p) => ({ ...p, restartOnServerQuit: e.target.checked }))} />*/} +
+
+ setServ((p) => ({ + ...p, + disableUpdateOnStart: e.target.checked, + convertValues: p.convertValues + }))}/>
+ {/* setServ((p) => ({ ...p, restartOnServerQuit: e.target.checked }))} />*/} - Custom server "dash" arguments (only use args like: -EnableIdlePlayerKick -ForceAllowCaveFlyers) - setServ((p) => ({ ...p, extraDashArgs: e.target.value, convertValues: p.convertValues }))}> - Custom server "questionmark" arguments (only use args like: ?PreventSpawnAnimations=true?PreventTribeAlliances=true) - setServ((p) => ({ ...p, extraQuestionmarkArguments: e.target.value, convertValues: p.convertValues }))}> -
+ Custom server "dash" arguments (only use args like: -EnableIdlePlayerKick + -ForceAllowCaveFlyers) + setServ((p) => ({ + ...p, + extraDashArgs: e.target.value, + convertValues: p.convertValues + }))}> + Custom server "questionmark" arguments (only use args like: + ?PreventSpawnAnimations=true?PreventTribeAlliances=true) + setServ((p) => ({ + ...p, + extraQuestionmarkArguments: e.target.value, + convertValues: p.convertValues + }))}>
- - - - Extra Settings - - - -
-
- - setServ((p) => ({ ...p, useIniConfig: e.target.checked, convertValues: p.convertValues }))} /> - -
- - setServ((p) => ({ ...p, discordWebHookEnabled: e.target.checked, convertValues: p.convertValues }))} /> - - +
+ + ) +} +function ExtraSettingsCard({setServ, serv}: {setServ: React.Dispatch>, serv: server.Server}) { + return ( + + + Extra Settings + + + +
+
+ + setServ((p) => ({ + ...p, + useIniConfig: e.target.checked, + convertValues: p.convertValues + }))}/> + +
+ + setServ((p) => ({ + ...p, + discordWebHookEnabled: e.target.checked, + convertValues: p.convertValues + }))}/> + + Discord webhook url - setServ((p) => ({ ...p, discordWebHook: e.target.value, convertValues: p.convertValues }))}> + setServ((p) => ({ + ...p, + discordWebHook: e.target.value, + convertValues: p.convertValues + }))}> - + -
-
+
+
+ ) +} +export function Administration({setServ, serv, onServerFilesDeleted}: Props) { + + + return ( + + + + ); } \ No newline at end of file diff --git a/frontend/src/pages/server/General.tsx b/frontend/src/pages/server/General.tsx index 97a8c6f..fabb811 100644 --- a/frontend/src/pages/server/General.tsx +++ b/frontend/src/pages/server/General.tsx @@ -20,8 +20,35 @@ type Props = { } +function ServerNameAndPasswordsCard({ setServ, serv }: {setServ: React.Dispatch>, serv: server.Server}) { + return ( + + + Server Name and Passwords + + + + Server Name: + setServ((p) => ({ ...p, serverName: e.target.value, convertValues: p.convertValues }))} > +
+
+ Server Password: + setServ((p) => ({ ...p, serverPassword: e.target.value, convertValues: p.convertValues }))} > +
+
+ Admin Password: + setServ((p) => ({ ...p, adminPassword: e.target.value, convertValues: p.convertValues }))} > +
+
+ Spectator Password: + setServ((p) => ({ ...p, spectatorPassword: e.target.value, convertValues: p.convertValues }))} > +
+
+
+ ) +} +function NetworkingCard({ setServ, serv }: {setServ: React.Dispatch>, serv: server.Server}) { -export function General({serv, setServ}: Props) { const defInter: {[p: string]: string} = { "none": "" } @@ -42,81 +69,59 @@ export function General({serv, setServ}: Props) { }, []); return ( - - {/* Server Name and Passwords */} - - - Server Name and Passwords - - - -{/* - dsadasd -*/} - - Server Name: - setServ((p) => ({ ...p, serverName: e.target.value, convertValues: p.convertValues }))} > -
-
- Server Password: - setServ((p) => ({ ...p, serverPassword: e.target.value, convertValues: p.convertValues }))} > -
-
- Admin Password: - setServ((p) => ({ ...p, adminPassword: e.target.value, convertValues: p.convertValues }))} > -
-
- Spectator Password: - setServ((p) => ({ ...p, spectatorPassword: e.target.value, convertValues: p.convertValues }))} > -
+ + + Networking + + + IP Address: + + Ports: +
+
+ Server Port: + (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, serverPort: parseInt(e.target.value), peerPort: parseInt(e.target.value) + 1, convertValues: p.convertValues })) : null} > +
+
+ Peer Port: +
- - - {/* Server Name and Passwords */} - - - Networking - - - IP Address: - - Ports: -
-
- Server Port: - (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, serverPort: parseInt(e.target.value), peerPort: parseInt(e.target.value) + 1, convertValues: p.convertValues })) : null} > -
-
- Peer Port: - -
-
- Query Port: - (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, queryPort: parseInt(e.target.value), convertValues: p.convertValues})) : null}> -
-
- RCON Port: - (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, rconPort: parseInt(e.target.value), convertValues: p.convertValues})) : null} type={'number'}> -
+
+ Query Port: + (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, queryPort: parseInt(e.target.value), convertValues: p.convertValues})) : null}>
- +
+ RCON Port: + (parseInt(e.target.value) >= 1 && parseInt(e.target.value) <= 65535) ? setServ((p) => ({ ...p, rconPort: parseInt(e.target.value), convertValues: p.convertValues})) : null} type={'number'}> +
+
+
+ ) +} + + +export function General({serv, setServ}: Props) { + return ( + + + ); } \ No newline at end of file diff --git a/frontend/src/pages/server/Settings.tsx b/frontend/src/pages/server/Settings.tsx index 74cb512..5aebb1a 100644 --- a/frontend/src/pages/server/Settings.tsx +++ b/frontend/src/pages/server/Settings.tsx @@ -1,46 +1,44 @@ import React from "react"; import {server} from "../../../wailsjs/go/models"; import { - Button, - Card, Checkbox, - DialogActions, - DialogContent, - DialogTitle, + Card, Divider, FormLabel, Input, - Modal, - ModalDialog, TabPanel, Typography } from "@mui/joy"; -import {IconAlertCircleFilled} from "@tabler/icons-react"; type Props = { setServ: React.Dispatch> serv: server.Server; +} + +function GeneralServerSettingsCard({ setServ, serv }: {setServ: React.Dispatch>, serv: server.Server}) { + return ( + + + General Server Settings + + +
+
+ Mods (e.g.: modid1,modid2,modid3) (for now you need to set "ACTIVEMODS" to ini manually) + setServ((p) => ({ ...p, mods: e.target.value, convertValues: p.convertValues }))}> + Player limit + { + if (parseInt(e.target.value) > 0) { + setServ((p) => ({...p, maxPlayers: parseInt(e.target.value), convertValues: p.convertValues})) + }}}> +
+
+
+ ) } export function Settings({setServ, serv}: Props) { return ( - - - General Server Settings - - - -
-
- Mods (e.g.: modid1,modid2,modid3) (for now you need to set "ACTIVEMODS" to ini manually) - setServ((p) => ({ ...p, mods: e.target.value, convertValues: p.convertValues }))}> - Player limit - { - if (parseInt(e.target.value) > 0) { - setServ((p) => ({...p, maxPlayers: parseInt(e.target.value), convertValues: p.convertValues})) - }}}> -
-
-
+
); } \ No newline at end of file diff --git a/helpers/updateServiceHelper.go b/helpers/app_update.go similarity index 100% rename from helpers/updateServiceHelper.go rename to helpers/app_update.go diff --git a/server/game.go b/server/game.go index 53696e9..e133598 100644 --- a/server/game.go +++ b/server/game.go @@ -277,13 +277,13 @@ func (s *Server) SaveGameIni() error { if err != nil { return err } + } - runtime.EventsEmit(s.ctx, "reloadServers") - } else { - err = gIni.ReflectFrom(&s.Game) - if err != nil { - return err - } + //modify ini file here + + err = gIni.ReflectFrom(&s.Game) + if err != nil { + return err } err = gIni.SaveTo(filepath.Join(s.ServerPath, "ShooterGame\\Saved\\Config\\WindowsServer\\Game.ini")) @@ -291,5 +291,7 @@ func (s *Server) SaveGameIni() error { return err } + runtime.EventsEmit(s.ctx, "reloadServers") + return nil } diff --git a/server/gus.go b/server/gus.go index a704196..c25d4f9 100644 --- a/server/gus.go +++ b/server/gus.go @@ -429,11 +429,6 @@ func (s *Server) SaveGameUserSettingsIni() error { return err } - /*err := gusIni.SaveTo(filepath.Join(s.ServerPath, "ShooterGame\\Saved\\Config\\WindowsServer\\GameUserSettings.ini")) - if err != nil { - return err - }*/ - } s.GameUserSettings.ServerSettings.RCONEnabled = true diff --git a/server/helpers.go b/server/helpers.go index bbc5861..9d7eb31 100644 --- a/server/helpers.go +++ b/server/helpers.go @@ -13,7 +13,11 @@ import ( "strconv" ) -// region Local Helpers +var iniOpts = ini.LoadOptions{ + AllowShadows: true, + AllowDuplicateShadowValues: true, +} + // findHighestKey returns the highest key in a map with int as key func findHighestKey(m map[int]*Server) int { var highestKey int = -1 @@ -190,10 +194,6 @@ func CopyAndMakeOld(path string) error { return nil } -//endregion - -//region Global and Frontend Helpers - func (c *ServerController) GetNetworkInterfacesIp() map[string]string { i, err := helpers.GetNetworkInterfaces() @@ -216,13 +216,6 @@ func (c *ServerController) GetServerDir() string { return c.serverDir } -//endregion - -var iniOpts = ini.LoadOptions{ - AllowShadows: true, - AllowDuplicateShadowValues: true, -} - func ensureFilePath(filePath string) error { // Check if the file already exists if _, err := os.Stat(filePath); os.IsNotExist(err) { diff --git a/server/server.go b/server/server.go index a2d6b1a..9dc5fac 100644 --- a/server/server.go +++ b/server/server.go @@ -48,14 +48,14 @@ type Server struct { //Server Name and Passwords ServerName string `json:"serverName"` - ServerPassword string `json:"serverPassword"` //TODO: Implement in startup/config + ServerPassword string `json:"serverPassword"` AdminPassword string `json:"adminPassword"` - SpectatorPassword string `json:"spectatorPassword"` //TODO: Implement in startup/config + SpectatorPassword string `json:"spectatorPassword"` //Server Networking IpAddress string `json:"ipAddress"` ServerPort int `json:"serverPort"` - PeerPort int `json:"peerPort"` //TODO: Implement in startup/config + PeerPort int `json:"peerPort"` //TODO: find out if used QueryPort int `json:"queryPort"` RCONPort int `json:"rconPort"` @@ -129,19 +129,6 @@ func (s *Server) Start() error { runtime.LogError(s.ctx, "Error sending message to discord: "+err.Error()) } } - - /*//restart server on crash - if err != nil && s.RestartOnServerQuit { - code := s.Command.ProcessState.ExitCode() - time.Sleep(2 * time.Second) - if code != 0 { - err := s.Start() - if err != nil { - runtime.EventsEmit(s.ctx, "onRestartServerFailed", err) - } - } - }*/ - }() } diff --git a/server/server_controller.go b/server/server_controller.go index c6096ac..0cda731 100644 --- a/server/server_controller.go +++ b/server/server_controller.go @@ -59,12 +59,12 @@ func (c *ServerController) Startup(ctx context.Context) { //endregion -//region Boilerplate functions +//region Frontend functions // GetAllServers gets all servers and saves them to ServerController.Servers and also returns them, if it fails it returns nil and error. If they already exist in the map it will just get that. func (c *ServerController) GetAllServers() (map[int]Server, error) { - servers, err := c.GetAllServersWithError() + servers, err := c.getAllServers() if err != nil { newErr := fmt.Errorf("Failed to get all servers " + c.serverDir + " error: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -85,7 +85,7 @@ func (c *ServerController) SaveServer(server Server) error { //TODO Make sure oldserv members get passed over to new instance since frontend will change all members even Command (which should not be updated by the frontend) - oldServ, err := c.GetServerWithError(server.Id, true) + oldServ, err := c.getServer(server.Id, true) if err != nil { newErr := fmt.Errorf("Error saving server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -95,7 +95,7 @@ func (c *ServerController) SaveServer(server Server) error { server.Command = oldServ.Command server.ctx = c.ctx - err = c.SaveServerWithError(&server) + err = c.saveServer(&server) if err != nil { newErr := fmt.Errorf("Error saving server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -106,7 +106,7 @@ func (c *ServerController) SaveServer(server Server) error { // GetServer returns the server with the given id if it does not exist it returns an empty server. This function checks the server dir too. If it does not exist in the map, and it does in the dir then it will add it to the map. It can error which is catch-able in the frontend func (c *ServerController) GetServer(id int) (Server, error) { - server, err := c.GetServerWithError(id, true) + server, err := c.getServer(id, true) if err != nil { newErr := fmt.Errorf("Failed getting server: " + strconv.Itoa(id) + " with: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -117,7 +117,7 @@ func (c *ServerController) GetServer(id int) (Server, error) { // CreateServer Creates a new server, returns it and adds it to the map. If it fails it returns an error which is catch-able in the Frontend. func (c *ServerController) CreateServer(saveToConfig bool) (Server, error) { - _, server, err := c.CreateServerWithError(saveToConfig) + _, server, err := c.createServer(saveToConfig) if err != nil { newErr := fmt.Errorf("Failed saving new server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -129,7 +129,7 @@ func (c *ServerController) CreateServer(saveToConfig bool) (Server, error) { // DeleteServerFiles deletes the server files from the server with the given id. If it fails it returns an error which is catch-able func (c *ServerController) DeleteServerFiles(id int) error { - err := c.DeleteServerFilesWithError(id) + err := c.deleteServerFiles(id) if err != nil { newErr := fmt.Errorf("Failed deleting server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -141,7 +141,7 @@ func (c *ServerController) DeleteServerFiles(id int) error { // DeleteProfile deletes the profile with the given id. If it fails it returns an error which is catch-able func (c *ServerController) DeleteProfile(id int) error { - err := c.DeleteProfileWithError(id) + err := c.deleteProfile(id) if err != nil { newErr := fmt.Errorf("Failed deleting profile: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -210,7 +210,7 @@ func (c *ServerController) SaveServerGus(id int, gus GameUserSettings) error { //TODO Make sure oldserv members get passed over to new instance since frontend will change all members even Command (which should not be updated by the frontend) - server, err := c.GetServerWithError(id, true) + server, err := c.getServer(id, true) if err != nil { newErr := fmt.Errorf("Error saving server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -220,7 +220,7 @@ func (c *ServerController) SaveServerGus(id int, gus GameUserSettings) error { server.GameUserSettings = gus server.ctx = c.ctx - err = c.SaveServerWithError(server) + err = c.saveServer(server) if err != nil { newErr := fmt.Errorf("Error saving server: " + err.Error()) runtime.LogError(c.ctx, newErr.Error()) @@ -231,7 +231,237 @@ func (c *ServerController) SaveServerGus(id int, gus GameUserSettings) error { //endregion -//region Server starting & stopping +//region Saving & loading + +// getServer returns the server with the given id if it does not exist it returns an empty server. This function checks the server dir too. If it does not exist in the map, and it does in the dir then it will add it to the map. +func (c *ServerController) getServer(id int, addToMap bool) (*Server, error) { + server, exists := c.Servers[id] + // If it does not exist in the map check the server dir + if !exists { + s, err := c.getServerFromDir(id, false) + if err != nil { + return nil, fmt.Errorf("Error getting server instance: %s", err.Error()) + } + + if addToMap { + c.Servers[id] = &s + } + + runtime.EventsEmit(c.ctx, "gotServer", s.Id) + s.ctx = c.ctx + return &s, nil + } else { + runtime.EventsEmit(c.ctx, "gotServer", server.Id) + server.ctx = c.ctx + return server, nil + } +} + +// createServer Creates a new server, returns it and adds it to the map, it also returns the key. If it fails it returns an error and an empty server and int -1 +func (c *ServerController) createServer(saveToConfig bool) (int, *Server, error) { + // get the highest in to generate new id + maxKey := findHighestKey(c.Servers) + id := maxKey + 1 + if _, exists := c.Servers[id]; exists { + return -1, nil, fmt.Errorf("Found a server with an key higher than the maximum key, max key: " + strconv.Itoa(maxKey) + " exists: " + strconv.Itoa(id)) + } + + // Check if it exists + if _, exists := c.Servers[id]; exists { + return -1, nil, fmt.Errorf("Found a server with new key in c.Servers key: " + strconv.Itoa(id)) + } + + NewServer := generateNewDefaultServer(id) + c.Servers[id] = &NewServer + + if saveToConfig { + err := c.saveServer(c.Servers[id]) + if err != nil { + // Don't handle this error because the server is already made, just mention it. + runtime.LogError(c.ctx, "Failed saving new server: "+err.Error()) + } + } + + runtime.EventsEmit(c.ctx, "serverCreated", NewServer.Id) + NewServer.ctx = c.ctx + + return id, &NewServer, nil +} + +// saveServer saves the server, and returns an error if it fails +func (c *ServerController) saveServer(server *Server) error { + + server.GameUserSettings.ServerSettings.RCONEnabled = true + server.GameUserSettings.ServerSettings.RCONPort = server.RCONPort + server.GameUserSettings.ServerSettings.ServerAdminPassword = server.AdminPassword + + server.GameUserSettings.SessionSettings.MultiHome = server.IpAddress + server.GameUserSettings.SessionSettings.Port = server.ServerPort + server.GameUserSettings.SessionSettings.QueryPort = server.QueryPort + server.GameUserSettings.SessionSettings.SessionName = server.ServerName + server.GameUserSettings.MultiHome.MultiHome = true + server.GameUserSettings.ScriptEngineGameSession.MaxPlayers = server.MaxPlayers + + // Check if server is correct. + if err := CheckIfServerCorrect(*server); err != nil { + return fmt.Errorf("Parsing server instance failed: " + err.Error()) + } + c.Servers[server.Id] = server + serverDir := path.Join(c.serverDir, strconv.Itoa(server.Id)) + + serverFile, err := json.MarshalIndent(server, "", " ") + if err != nil { + return fmt.Errorf("Error marshalling config file: " + err.Error()) + } + + // If config dir does not exist create a new one + _, err = os.Stat(serverDir) + if err != nil { + if os.IsNotExist(err) { + runtime.LogDebug(c.ctx, "Server config "+strconv.Itoa(server.Id)+" directory does not exist, creating it") + if err := os.MkdirAll(serverDir, os.ModePerm); err != nil { + return fmt.Errorf("Error creating server dir: " + serverDir + "with error: " + err.Error()) + } + } + } + + err = CopyAndMakeOld(path.Join(serverDir, configFileName)) + if err != nil { + return err + } + + err = os.WriteFile(path.Join(serverDir, configFileName), serverFile, 0644) + if err != nil { + return fmt.Errorf("Error writing config file: " + err.Error()) + } + + runtime.EventsEmit(c.ctx, "serverSaved", server.Id) + + return nil +} + +// getAllServers gets all servers and saves them to ServerController.Servers and also returns them, if it fails it returns nil and error. If they already exist in the map it will just get that. +func (c *ServerController) getAllServers() (map[int]*Server, error) { + + allServerDir := c.serverDir + + if _, err := os.Stat(allServerDir); err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf(allServerDir + " does not exit") + } + return nil, fmt.Errorf("Getting all servers from directory failed: " + err.Error()) + } + + children, err := os.ReadDir(allServerDir) + if err != nil { + return nil, fmt.Errorf("Failed to read children in " + c.serverDir + " error: " + err.Error()) + } + + servers := make(map[int]*Server) + + for _, child := range children { + if child.IsDir() { + index, err := strconv.Atoi(child.Name()) + if err != nil { + return nil, fmt.Errorf("Failed to parse to int: " + child.Name() + " error: " + err.Error()) + } + + server, err := c.getServer(index, false) + if err != nil { + return nil, fmt.Errorf("Failed to get server: " + child.Name() + " error: " + err.Error()) + } + + servers[index] = server + } + } + + c.Servers = servers + + return c.Servers, nil +} + +// deleteServerFiles deletes the server files from the server with the given id. If it fails it returns an error which is catch-able +func (c *ServerController) deleteServerFiles(id int) error { + server, err := c.getServer(id, false) + if err != nil { + return fmt.Errorf("Failed to get server: " + strconv.Itoa(id) + " error: " + err.Error()) + } + serverPath := server.ServerPath + + err = os.RemoveAll(serverPath) + if err != nil { + return fmt.Errorf("Failed to delete server " + strconv.Itoa(id) + " files: " + serverPath + " error: " + err.Error()) + } + + return nil +} + +// deleteProfile deletes the profile with the given id. If it fails it returns an error which is catch-able +func (c *ServerController) deleteProfile(id int) error { + + serverDir := path.Join(c.serverDir, strconv.Itoa(id)) + + err := os.RemoveAll(serverDir) + if err != nil { + return fmt.Errorf("Failed to delete server " + strconv.Itoa(id) + " profile error: " + err.Error()) + } + + delete(c.Servers, id) + + return nil +} + +// GetAllServersFromDir gets all servers from dir and saves them to ServerController.Servers and also returns them, if it fails it returns nil and an error which is catch-able in the frontend. This will overwrite c.Servers! +func (c *ServerController) GetAllServersFromDir() (map[int]*Server, error) { + allserverDir := c.serverDir + + if _, err := os.Stat(allserverDir); err != nil { + if os.IsNotExist(err) { + newErr := fmt.Errorf(allserverDir + " does not exit") + runtime.LogError(c.ctx, newErr.Error()) + return nil, newErr + } + newErr := fmt.Errorf("Getting all servers from directory failed: " + err.Error()) + runtime.LogError(c.ctx, newErr.Error()) + return nil, newErr + } + + children, err := os.ReadDir(allserverDir) + if err != nil { + newErr := fmt.Errorf("Failed to read children in " + c.serverDir + " error: " + err.Error()) + runtime.LogError(c.ctx, newErr.Error()) + return nil, newErr + } + + servers := make(map[int]*Server) + + for _, child := range children { + if child.IsDir() { + index, err := strconv.Atoi(child.Name()) + if err != nil { + newErr := fmt.Errorf("Failed to parse to int: " + child.Name() + " error: " + err.Error()) + runtime.LogError(c.ctx, newErr.Error()) + return nil, newErr + } + + server, err := c.getServerFromDir(index, false) + if err != nil { + newErr := fmt.Errorf("Failed to get server: " + child.Name() + " error: " + err.Error()) + runtime.LogError(c.ctx, newErr.Error()) + return nil, newErr + } + servers[index] = &server + } + } + + c.Servers = servers + + return c.Servers, nil +} + +//endregion + +//region starting & stopping // StartServer starts the server. It will only start a server that exists in the map func (c *ServerController) StartServer(id int) error { @@ -308,8 +538,6 @@ func (c *ServerController) GetServerStatus(id int) (bool, error) { //region ServerController helper functions // CheckServerInstalled Checks if the server dir exists. -// -// TODO: This should check more in depth. func (c *ServerController) CheckServerInstalled(id int) (bool, error) { server := c.Servers[id] @@ -324,7 +552,7 @@ func (c *ServerController) CheckServerInstalled(id int) (bool, error) { } func (c *ServerController) GetServerStartupCommand(id int) (string, error) { - server, err := c.GetServerWithError(id, false) + server, err := c.getServer(id, false) if err != nil { err := fmt.Errorf("error getting server startup command " + strconv.Itoa(id) + ": server does not exist") runtime.LogError(c.ctx, err.Error()) diff --git a/server/server_controller_werr.go b/server/server_controller_werr.go deleted file mode 100644 index c27a8e9..0000000 --- a/server/server_controller_werr.go +++ /dev/null @@ -1,240 +0,0 @@ -package server - -import ( - "encoding/json" - "fmt" - "github.com/wailsapp/wails/v2/pkg/runtime" - "os" - "path" - "strconv" -) - -//TODO refactor somehow -//THIS FILE CONTAINS FUNCTIONS THAT SHOULD NOT BE CALLED BY THE FRONTEND AKA WITH ERROR (returns error without logging) -//This is because these functions don't log errors to the log, the wrapper functions in server_controller.go are used for this - -// GetServerWithError returns the server with the given id if it does not exist it returns an empty server. This function checks the server dir too. If it does not exist in the map, and it does in the dir then it will add it to the map. -func (c *ServerController) GetServerWithError(id int, addToMap bool) (*Server, error) { - server, exists := c.Servers[id] - // If it does not exist in the map check the server dir - if !exists { - s, err := c.getServerFromDir(id, false) - if err != nil { - return nil, fmt.Errorf("Error getting server instance: %s", err.Error()) - } - - if addToMap { - c.Servers[id] = &s - } - - runtime.EventsEmit(c.ctx, "gotServer", s.Id) - s.ctx = c.ctx - return &s, nil - } else { - runtime.EventsEmit(c.ctx, "gotServer", server.Id) - server.ctx = c.ctx - return server, nil - } -} - -// CreateServerWithError Creates a new server, returns it and adds it to the map, it also returns the key. If it fails it returns an error and an empty server and int -1 -func (c *ServerController) CreateServerWithError(saveToConfig bool) (int, *Server, error) { - // get the highest in to generate new id - maxKey := findHighestKey(c.Servers) - id := maxKey + 1 - if _, exists := c.Servers[id]; exists { - return -1, nil, fmt.Errorf("Found a server with an key higher than the maximum key, max key: " + strconv.Itoa(maxKey) + " exists: " + strconv.Itoa(id)) - } - - // Check if it exists - if _, exists := c.Servers[id]; exists { - return -1, nil, fmt.Errorf("Found a server with new key in c.Servers key: " + strconv.Itoa(id)) - } - - NewServer := generateNewDefaultServer(id) - c.Servers[id] = &NewServer - - if saveToConfig { - err := c.SaveServerWithError(c.Servers[id]) - if err != nil { - // Don't handle this error because the server is already made, just mention it. - runtime.LogError(c.ctx, "Failed saving new server: "+err.Error()) - } - } - - runtime.EventsEmit(c.ctx, "serverCreated", NewServer.Id) - NewServer.ctx = c.ctx - - return id, &NewServer, nil -} - -// SaveServerWithError saves the server, and returns an error if it fails -func (c *ServerController) SaveServerWithError(server *Server) error { - - server.GameUserSettings.ServerSettings.RCONEnabled = true - server.GameUserSettings.ServerSettings.RCONPort = server.RCONPort - server.GameUserSettings.ServerSettings.ServerAdminPassword = server.AdminPassword - - server.GameUserSettings.SessionSettings.MultiHome = server.IpAddress - server.GameUserSettings.SessionSettings.Port = server.ServerPort - server.GameUserSettings.SessionSettings.QueryPort = server.QueryPort - server.GameUserSettings.SessionSettings.SessionName = server.ServerName - server.GameUserSettings.MultiHome.MultiHome = true - server.GameUserSettings.ScriptEngineGameSession.MaxPlayers = server.MaxPlayers - - // Check if server is correct. - if err := CheckIfServerCorrect(*server); err != nil { - return fmt.Errorf("Parsing server instance failed: " + err.Error()) - } - c.Servers[server.Id] = server - serverDir := path.Join(c.serverDir, strconv.Itoa(server.Id)) - - serverFile, err := json.MarshalIndent(server, "", " ") - if err != nil { - return fmt.Errorf("Error marshalling config file: " + err.Error()) - } - - // If config dir does not exist create a new one - _, err = os.Stat(serverDir) - if err != nil { - if os.IsNotExist(err) { - runtime.LogDebug(c.ctx, "Server config "+strconv.Itoa(server.Id)+" directory does not exist, creating it") - if err := os.MkdirAll(serverDir, os.ModePerm); err != nil { - return fmt.Errorf("Error creating server dir: " + serverDir + "with error: " + err.Error()) - } - } - } - - err = CopyAndMakeOld(path.Join(serverDir, configFileName)) - if err != nil { - return err - } - - err = os.WriteFile(path.Join(serverDir, configFileName), serverFile, 0644) - if err != nil { - return fmt.Errorf("Error writing config file: " + err.Error()) - } - - runtime.EventsEmit(c.ctx, "serverSaved", server.Id) - - return nil -} - -// GetAllServersWithError gets all servers and saves them to ServerController.Servers and also returns them, if it fails it returns nil and error. If they already exist in the map it will just get that. -func (c *ServerController) GetAllServersWithError() (map[int]*Server, error) { - - allServerDir := c.serverDir - - if _, err := os.Stat(allServerDir); err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf(allServerDir + " does not exit") - } - return nil, fmt.Errorf("Getting all servers from directory failed: " + err.Error()) - } - - children, err := os.ReadDir(allServerDir) - if err != nil { - return nil, fmt.Errorf("Failed to read children in " + c.serverDir + " error: " + err.Error()) - } - - servers := make(map[int]*Server) - - for _, child := range children { - if child.IsDir() { - index, err := strconv.Atoi(child.Name()) - if err != nil { - return nil, fmt.Errorf("Failed to parse to int: " + child.Name() + " error: " + err.Error()) - } - - server, err := c.GetServerWithError(index, false) - if err != nil { - return nil, fmt.Errorf("Failed to get server: " + child.Name() + " error: " + err.Error()) - } - - servers[index] = server - } - } - - c.Servers = servers - - return c.Servers, nil -} - -// GetAllServersFromDir gets all servers from dir and saves them to ServerController.Servers and also returns them, if it fails it returns nil and an error which is catch-able in the frontend. This will overwrite c.Servers! -func (c *ServerController) GetAllServersFromDir() (map[int]*Server, error) { - allserverDir := c.serverDir - - if _, err := os.Stat(allserverDir); err != nil { - if os.IsNotExist(err) { - newErr := fmt.Errorf(allserverDir + " does not exit") - runtime.LogError(c.ctx, newErr.Error()) - return nil, newErr - } - newErr := fmt.Errorf("Getting all servers from directory failed: " + err.Error()) - runtime.LogError(c.ctx, newErr.Error()) - return nil, newErr - } - - children, err := os.ReadDir(allserverDir) - if err != nil { - newErr := fmt.Errorf("Failed to read children in " + c.serverDir + " error: " + err.Error()) - runtime.LogError(c.ctx, newErr.Error()) - return nil, newErr - } - - servers := make(map[int]*Server) - - for _, child := range children { - if child.IsDir() { - index, err := strconv.Atoi(child.Name()) - if err != nil { - newErr := fmt.Errorf("Failed to parse to int: " + child.Name() + " error: " + err.Error()) - runtime.LogError(c.ctx, newErr.Error()) - return nil, newErr - } - - server, err := c.getServerFromDir(index, false) - if err != nil { - newErr := fmt.Errorf("Failed to get server: " + child.Name() + " error: " + err.Error()) - runtime.LogError(c.ctx, newErr.Error()) - return nil, newErr - } - servers[index] = &server - } - } - - c.Servers = servers - - return c.Servers, nil -} - -// DeleteServerFilesWithError deletes the server files from the server with the given id. If it fails it returns an error which is catch-able -func (c *ServerController) DeleteServerFilesWithError(id int) error { - server, err := c.GetServerWithError(id, false) - if err != nil { - return fmt.Errorf("Failed to get server: " + strconv.Itoa(id) + " error: " + err.Error()) - } - serverPath := server.ServerPath - - err = os.RemoveAll(serverPath) - if err != nil { - return fmt.Errorf("Failed to delete server " + strconv.Itoa(id) + " files: " + serverPath + " error: " + err.Error()) - } - - return nil -} - -// DeleteProfileWithError deletes the profile with the given id. If it fails it returns an error which is catch-able -func (c *ServerController) DeleteProfileWithError(id int) error { - - serverDir := path.Join(c.serverDir, strconv.Itoa(id)) - - err := os.RemoveAll(serverDir) - if err != nil { - return fmt.Errorf("Failed to delete server " + strconv.Itoa(id) + " profile error: " + err.Error()) - } - - delete(c.Servers, id) - - return nil -}