Added recent scripts subpage of active scripts

This commit is contained in:
Olivier Gagnon 2021-10-23 16:04:00 -04:00
parent 6d39fda3fa
commit c34d53bc14
9 changed files with 313 additions and 73 deletions

36
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,27 @@
import { RunningScript } from "src/Script/RunningScript";
import { WorkerScript } from "./WorkerScript";
export const recentScripts: RecentScript[] = [];
export function AddRecentScript(workerScript: WorkerScript): void {
if (recentScripts.find((r) => r.pid === workerScript.pid)) return;
recentScripts.push({
filename: workerScript.name,
args: workerScript.args,
pid: workerScript.pid,
timestamp: new Date(),
runningScript: workerScript.scriptRef,
});
while (recentScripts.length > 50) {
recentScripts.pop();
}
}
export interface RecentScript {
filename: string;
args: string[];
pid: number;
timestamp: Date;
runningScript: RunningScript;
}

@ -11,6 +11,7 @@ import { GetServer } from "../Server/AllServers";
import { compareArrays } from "../utils/helpers/compareArrays"; import { compareArrays } from "../utils/helpers/compareArrays";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { AddRecentScript } from "./RecentScripts";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean; export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean; export function killWorkerScript(workerScript: WorkerScript): boolean;
@ -85,49 +86,44 @@ function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = tru
* Helper function that removes the script being killed from the global pool. * Helper function that removes the script being killed from the global pool.
* Also handles other cleanup-time operations * Also handles other cleanup-time operations
* *
* @param {WorkerScript | number} - Identifier for WorkerScript. Either the object itself, or * @param {WorkerScript} - Identifier for WorkerScript. Either the object itself, or
* its index in the global workerScripts array * its index in the global workerScripts array
*/ */
function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void { function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
if (workerScript instanceof WorkerScript) { const ip = workerScript.hostname;
const ip = workerScript.hostname; const name = workerScript.name;
const name = workerScript.name;
// Get the server on which the script runs // Get the server on which the script runs
const server = GetServer(ip); const server = GetServer(ip);
if (server == null) { if (server == null) {
console.error(`Could not find server on which this script is running: ${ip}`); console.error(`Could not find server on which this script is running: ${ip}`);
return;
}
// Delete the RunningScript object from that server
for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScript = server.runningScripts[i];
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
server.runningScripts.splice(i, 1);
break;
}
}
// Recalculate ram used on that server
server.ramUsed = 0;
for (const rs of server.runningScripts) server.ramUsed += rs.ramUsage * rs.threads;
// Delete script from global pool (workerScripts)
const res = workerScripts.delete(workerScript.pid);
if (!res) {
console.warn(`removeWorkerScript() called with WorkerScript that wasn't in the global map:`);
console.warn(workerScript);
}
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emit();
}
} else {
console.error(`Invalid argument passed into removeWorkerScript():`);
console.error(workerScript);
return; return;
} }
// Delete the RunningScript object from that server
for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScript = server.runningScripts[i];
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
server.runningScripts.splice(i, 1);
break;
}
}
// Recalculate ram used on that server
server.ramUsed = 0;
for (const rs of server.runningScripts) server.ramUsed += rs.ramUsage * rs.threads;
// Delete script from global pool (workerScripts)
const res = workerScripts.delete(workerScript.pid);
if (!res) {
console.warn(`removeWorkerScript() called with WorkerScript that wasn't in the global map:`);
console.warn(workerScript);
}
AddRecentScript(workerScript);
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emit();
}
} }
/** /**

@ -0,0 +1,31 @@
/**
* Root React Component for the "Active Scripts" UI page. This page displays
* and provides information about all of the player's scripts that are currently running
*/
import React from "react";
import { ScriptProduction } from "./ScriptProduction";
import { ServerAccordions } from "./ServerAccordions";
import { WorkerScript } from "../../Netscript/WorkerScript";
import Typography from "@mui/material/Typography";
interface IProps {
workerScripts: Map<number, WorkerScript>;
}
export function ActiveScriptsPage(props: IProps): React.ReactElement {
return (
<>
<Typography>
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the servers
on which they are running.
</Typography>
<ScriptProduction />
<ServerAccordions {...props} />
</>
);
}

@ -3,17 +3,16 @@
* and provides information about all of the player's scripts that are currently running * and provides information about all of the player's scripts that are currently running
*/ */
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { ScriptProduction } from "./ScriptProduction"; import { ActiveScriptsPage } from "./ActiveScriptsPage";
import { ServerAccordions } from "./ServerAccordions"; import { RecentScriptsPage } from "./RecentScriptsPage";
import { WorkerScript } from "../../Netscript/WorkerScript"; import { WorkerScript } from "../../Netscript/WorkerScript";
import Typography from "@mui/material/Typography"; interface IProps {
type IProps = {
workerScripts: Map<number, WorkerScript>; workerScripts: Map<number, WorkerScript>;
}; }
export function ActiveScriptsRoot(props: IProps): React.ReactElement { export function ActiveScriptsRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -26,17 +25,19 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const [tab, setTab] = useState<"active" | "recent">("active");
function handleChange(event: React.SyntheticEvent, tab: "active" | "recent"): void {
setTab(tab);
}
return ( return (
<> <>
<Typography variant="h4">Active Scripts</Typography> <Tabs variant="fullWidth" value={tab} onChange={handleChange}>
<Typography> <Tab label={"Active"} value={"active"} />
This page displays a list of all of your scripts that are currently running across every machine. It also <Tab label={"Recent"} value={"recent"} />
provides information about each script's production. The scripts are categorized by the hostname of the servers </Tabs>
on which they are running.
</Typography>
<ScriptProduction /> {tab === "active" && <ActiveScriptsPage workerScripts={props.workerScripts} />}
<ServerAccordions {...props} /> {tab === "recent" && <RecentScriptsPage />}
</> </>
); );
} }

@ -0,0 +1,165 @@
/**
* React Component for displaying a single WorkerScript's info as an
* Accordion element
*/
import * as React from "react";
import { numeralWrapper } from "../numeralFormat";
import Table from "@mui/material/Table";
import TableCell from "@mui/material/TableCell";
import TableRow from "@mui/material/TableRow";
import TableBody from "@mui/material/TableBody";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import makeStyles from "@mui/styles/makeStyles";
import Collapse from "@mui/material/Collapse";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { arrayToString } from "../../utils/helpers/arrayToString";
import { Money } from "../React/Money";
import { MoneyRate } from "../React/MoneyRate";
import { RecentScript } from "../..//Netscript/RecentScripts";
import { LogBoxEvents } from "../React/LogBoxManager";
const useStyles = makeStyles({
noborder: {
borderBottom: "none",
},
});
interface IProps {
recentScript: RecentScript;
}
export function RecentScriptAccordion(props: IProps): React.ReactElement {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const recentScript = props.recentScript;
// Calculations for script stats
const onlineMps = recentScript.runningScript.onlineMoneyMade / recentScript.runningScript.onlineRunningTime;
const onlineEps = recentScript.runningScript.onlineExpGained / recentScript.runningScript.onlineRunningTime;
function logClickHandler(): void {
LogBoxEvents.emit(recentScript.runningScript);
}
return (
<>
<ListItemButton onClick={() => setOpen((old) => !old)} component={Paper}>
<ListItemText
primary={
<Typography>
{recentScript.filename} (died{" "}
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timestamp.getTime())} ago)
</Typography>
}
/>
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} timeout={0} unmountOnExit>
<Box mx={6}>
<Table padding="none" size="small">
<TableBody>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Threads:</Typography>
</TableCell>
<TableCell className={classes.noborder}>
<Typography>{numeralWrapper.formatThreads(recentScript.runningScript.threads)}</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder} colSpan={2}>
<Typography> Args: {arrayToString(recentScript.args)}</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Online Time:</Typography>
</TableCell>
<TableCell className={classes.noborder}>
<Typography>
{convertTimeMsToTimeElapsedString(recentScript.runningScript.onlineRunningTime * 1e3)}
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Offline Time:</Typography>
</TableCell>
<TableCell className={classes.noborder}>
<Typography>
{convertTimeMsToTimeElapsedString(recentScript.runningScript.offlineRunningTime * 1e3)}
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Total online production:</Typography>
</TableCell>
<TableCell className={classes.noborder} align="left">
<Typography>
<Money money={recentScript.runningScript.onlineMoneyMade} />
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder} colSpan={1} />
<TableCell className={classes.noborder} align="left">
<Typography>
&nbsp;{numeralWrapper.formatExp(recentScript.runningScript.onlineExpGained) + " hacking exp"}
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Online production rate:</Typography>
</TableCell>
<TableCell className={classes.noborder} align="left">
<Typography>
<MoneyRate money={onlineMps} />
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder} colSpan={1} />
<TableCell className={classes.noborder} align="left">
<Typography>&nbsp;{numeralWrapper.formatExp(onlineEps) + " hacking exp / sec"}</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder}>
<Typography> Total offline production:</Typography>
</TableCell>
<TableCell className={classes.noborder} align="left">
<Typography>
<Money money={recentScript.runningScript.offlineMoneyMade} />
</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.noborder} colSpan={1} />
<TableCell className={classes.noborder} align="left">
<Typography>
&nbsp;{numeralWrapper.formatExp(recentScript.runningScript.offlineExpGained) + " hacking exp"}
</Typography>
</TableCell>
</TableRow>
</TableBody>
</Table>
<Button onClick={logClickHandler}>LOG</Button>
</Box>
</Collapse>
</>
);
}

@ -0,0 +1,20 @@
/**
* Root React Component for the "Active Scripts" UI page. This page displays
* and provides information about all of the player's scripts that are currently running
*/
import React from "react";
import Typography from "@mui/material/Typography";
import { recentScripts } from "../../Netscript/RecentScripts";
import { RecentScriptAccordion } from "./RecentScriptAccordion";
export function RecentScriptsPage(): React.ReactElement {
return (
<>
<Typography>List of all recently killed scripts.</Typography>
{recentScripts.map((r) => (
<RecentScriptAccordion key={r.pid} recentScript={r} />
))}
</>
);
}