Add setting UI for port/reconnect, swap wrong API handlers

This commit is contained in:
Zoë Hoekstra 2022-07-31 19:53:15 +02:00
parent d20f621b47
commit 5fc67c328b
No known key found for this signature in database
GPG Key ID: F9B7B7D8130F3323
7 changed files with 127 additions and 52 deletions

@ -0,0 +1,46 @@
import React from "react";
interface baubleState {
connection: boolean;
callback: () => boolean;
}
interface baubleProps {
callback: () => boolean;
}
export class ConnectionBauble extends React.Component<baubleProps> {
timerID: NodeJS.Timer;
state: baubleState;
constructor(props: baubleProps) {
super(props);
this.state = {
connection: false,
callback: props.callback
};
}
componentDidMount() : void {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() : void {
clearInterval(this.timerID);
}
tick() : void {
this.setState({
connection: this.state.callback()
});
}
render() : string {
return (
this.state.connection? "Connected" : "Disconnected"
);
}
}

@ -1,12 +1,15 @@
import { MenuItem, Select, SelectChangeEvent, TextField, Tooltip, Typography } from "@mui/material"; import { MenuItem, Select, SelectChangeEvent, TextField, Tooltip, Typography, Box } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { isRemoteFileApiConnectionLive, newRemoteFileApiConnection } from "../../RemoteFileAPI/RemoteFileAPI";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { OptionSwitch } from "../../ui/React/OptionSwitch"; import { OptionSwitch } from "../../ui/React/OptionSwitch";
import { formatTime } from "../../utils/helpers/formatTime"; import { formatTime } from "../../utils/helpers/formatTime";
import { GameOptionsTab } from "../GameOptionsTab"; import { GameOptionsTab } from "../GameOptionsTab";
import { GameOptionsPage } from "./GameOptionsPage"; import { GameOptionsPage } from "./GameOptionsPage";
import { OptionsSlider } from "./OptionsSlider"; import { OptionsSlider } from "./OptionsSlider";
import Button from "@mui/material/Button";
import { ConnectionBauble } from "./ConnectionBauble";
interface IProps { interface IProps {
currentTab: GameOptionsTab; currentTab: GameOptionsTab;
@ -21,6 +24,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity); const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
const [autosaveInterval, setAutosaveInterval] = useState(Settings.AutosaveInterval); const [autosaveInterval, setAutosaveInterval] = useState(Settings.AutosaveInterval);
const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat); const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat);
const [remoteFileApiPort, setRemoteFileApiPort] = useState(Settings.RemoteFileApiPort);
const [locale, setLocale] = useState(Settings.Locale); const [locale, setLocale] = useState(Settings.Locale);
function handleExecTimeChange( function handleExecTimeChange(
@ -81,6 +85,11 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
Settings.TimestampsFormat = event.target.value; Settings.TimestampsFormat = event.target.value;
} }
function handleRemoteFileApiPortChange(event: React.ChangeEvent<HTMLInputElement>): void {
setRemoteFileApiPort(Number(event.target.value) as number);
Settings.RemoteFileApiPort = Number(event.target.value);
}
const pages = { const pages = {
[GameOptionsTab.SYSTEM]: ( [GameOptionsTab.SYSTEM]: (
<GameOptionsPage title="System"> <GameOptionsPage title="System">
@ -361,6 +370,35 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
</> </>
} }
/> />
<Tooltip title={
<Typography>
This port number is used to connect to a Remote File API port,
please ensure that it matches with the port the Remote File API server is publishing on (12525 by default).
Click the reconnect button to try and re-establish connection.
The little colored bauble shows whether the connection is live or not.
</Typography>
}>
<TextField
InputProps={{
startAdornment: (
<Typography
color={remoteFileApiPort > 0 && remoteFileApiPort <= 65535? "success" : "error"}
>
Remote File API port:
</Typography>
),
endAdornment: (
<Box>
<Button onClick={newRemoteFileApiConnection}>Reconnect</Button>
<ConnectionBauble callback={isRemoteFileApiConnectionLive}/>
</Box>
),
}}
value={remoteFileApiPort}
onChange={handleRemoteFileApiPortChange}
placeholder="12525"
/>
</Tooltip>
</GameOptionsPage> </GameOptionsPage>
), ),
}; };

@ -83,12 +83,12 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => void | R
const server = GetServer(msg.params.server); const server = GetServer(msg.params.server);
if (server == null) return error("Server hostname invalid", msg); if (server == null) return error("Server hostname invalid", msg);
const fileList: FileContent[] = [ const fileNameList: string[] = [
...server.textFiles.map((txt): FileContent => { return { filename: txt.filename, content: txt.text } }), ...server.textFiles.map((txt): string => txt.filename),
...server.scripts.map((scr): FileContent => { return { filename: scr.filename, content: scr.code } }) ...server.scripts.map((scr): string => scr.filename)
]; ];
return new RFAMessage({ result: JSON.stringify(fileList), id: msg.id }); return new RFAMessage({ result: JSON.stringify(fileNameList), id: msg.id });
}, },
getAllFiles: function (msg: RFAMessage): RFAMessage { getAllFiles: function (msg: RFAMessage): RFAMessage {
@ -97,12 +97,12 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => void | R
const server = GetServer(msg.params.server); const server = GetServer(msg.params.server);
if (server == null) return error("Server hostname invalid", msg); if (server == null) return error("Server hostname invalid", msg);
const fileNameList: string[] = [ const fileList: FileContent[] = [
...server.textFiles.map((txt): string => txt.filename), ...server.textFiles.map((txt): FileContent => { return { filename: txt.filename, content: txt.text } }),
...server.scripts.map((scr): string => scr.filename) ...server.scripts.map((scr): FileContent => { return { filename: scr.filename, content: scr.code } })
]; ];
return new RFAMessage({ result: JSON.stringify(fileNameList), id: msg.id }); return new RFAMessage({ result: JSON.stringify(fileList), id: msg.id });
}, },
calculateRam: function (msg: RFAMessage): RFAMessage { calculateRam: function (msg: RFAMessage): RFAMessage {

@ -13,36 +13,18 @@ export class Remote {
this.port = port; this.port = port;
} }
public stopConnection() : void {
this.connection?.close();
}
public startConnection() : void { public startConnection() : void {
this.startConnectionRecurse(1, 5);
}
handleCloseEvent():void {
delete this.connection;
RFALogger.log("Connection closed.");
}
startConnectionRecurse(retryN : number, retryMax : number) : void {
RFALogger.log("Trying to connect."); RFALogger.log("Trying to connect.");
this.connection = new WebSocket(this.protocol + "://" + this.ipaddr + ":" + this.port); this.connection = new WebSocket(this.protocol + "://" + this.ipaddr + ":" + this.port);
this.connection.addEventListener("error", (e:Event) => { this.connection.addEventListener("error", (e:Event) => RFALogger.error(e));
if(!this.connection) return;
// When having trouble connecting, try again.
if (this.connection.readyState === 3) {
RFALogger.log(`Connection lost, retrying (try #${retryN}).`);
if (retryN <= retryMax) this.startConnectionRecurse(retryN + 1, retryMax);
return;
}
// Else handle the error normally
RFALogger.error(e);
});
this.connection.addEventListener("message", handleMessageEvent); this.connection.addEventListener("message", handleMessageEvent);
this.connection.addEventListener("open", () => RFALogger.log("Connection established: ", this.ipaddr, ":", this.port)); this.connection.addEventListener("open", () => RFALogger.log("Connection established: ", this.ipaddr, ":", this.port));
this.connection.addEventListener("close", this.handleCloseEvent); this.connection.addEventListener("close", () => RFALogger.log("Connection closed"));
} }
} }

@ -1,16 +1,19 @@
import { Settings } from "../Settings/Settings";
import { Remote } from "./Remote"; import { Remote } from "./Remote";
class RemoteFileAPI {
server : Remote;
constructor(){ let server: Remote;
this.server = new Remote("localhost", 12525);
return;
}
enable() : void { export function newRemoteFileApiConnection() : void {
this.server.startConnection(); if(server == undefined)
server = new Remote("localhost", Settings.RemoteFileApiPort);
else {
server.stopConnection();
server = new Remote("localhost", Settings.RemoteFileApiPort);
server.startConnection();
} }
} }
export const RFA = new RemoteFileAPI; export function isRemoteFileApiConnectionLive() : boolean {
return server.connection != undefined && server.connection.readyState == 1;
}

@ -84,6 +84,11 @@ interface IDefaultSettings {
*/ */
MaxTerminalCapacity: number; MaxTerminalCapacity: number;
/**
* Port the Remote File API client will try to connect to.
*/
RemoteFileApiPort: number;
/** /**
* Save the game when you save any file. * Save the game when you save any file.
*/ */
@ -206,6 +211,7 @@ export const defaultSettings: IDefaultSettings = {
MaxLogCapacity: 50, MaxLogCapacity: 50,
MaxPortCapacity: 50, MaxPortCapacity: 50,
MaxTerminalCapacity: 500, MaxTerminalCapacity: 500,
RemoteFileApiPort: 12525,
SaveGameOnFileSave: true, SaveGameOnFileSave: true,
SuppressBuyAugmentationConfirmation: false, SuppressBuyAugmentationConfirmation: false,
SuppressFactionInvites: false, SuppressFactionInvites: false,
@ -248,6 +254,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity, MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity,
OwnedAugmentationsOrder: OwnedAugmentationsOrderSetting.AcquirementTime, OwnedAugmentationsOrder: OwnedAugmentationsOrderSetting.AcquirementTime,
PurchaseAugmentationsOrder: PurchaseAugmentationsOrderSetting.Default, PurchaseAugmentationsOrder: PurchaseAugmentationsOrderSetting.Default,
RemoteFileApiPort: defaultSettings.RemoteFileApiPort,
SaveGameOnFileSave: defaultSettings.SaveGameOnFileSave, SaveGameOnFileSave: defaultSettings.SaveGameOnFileSave,
SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation, SuppressBuyAugmentationConfirmation: defaultSettings.SuppressBuyAugmentationConfirmation,
SuppressFactionInvites: defaultSettings.SuppressFactionInvites, SuppressFactionInvites: defaultSettings.SuppressFactionInvites,

@ -5,7 +5,7 @@ import { TTheme as Theme, ThemeEvents, refreshTheme } from "./Themes/ui/Theme";
import { LoadingScreen } from "./ui/LoadingScreen"; import { LoadingScreen } from "./ui/LoadingScreen";
import { initElectron } from "./Electron"; import { initElectron } from "./Electron";
import { RFA } from "./RemoteFileAPI/RemoteFileAPI"; import { newRemoteFileApiConnection } from "./RemoteFileAPI/RemoteFileAPI";
initElectron(); initElectron();
globalThis["React"] = React; globalThis["React"] = React;
@ -17,8 +17,7 @@ ReactDOM.render(
document.getElementById("root"), document.getElementById("root"),
); );
console.log("[RFA] Fix this hack ASAP"); newRemoteFileApiConnection();
RFA.enable();
function rerender(): void { function rerender(): void {
refreshTheme(); refreshTheme();