Servers are sorted on Active Scripts page (#1102)

This commit is contained in:
Snarling
2024-02-16 21:06:18 -05:00
committed by GitHub
parent 8c8af38a3a
commit 9b483bdd8a
4 changed files with 58 additions and 38 deletions

View File

@@ -53,6 +53,8 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
// Flag indicating whether this is a purchased server
purchasedByPlayer = true;
isHacknetServer = true;
constructor(params: IConstructorParams = { hostname: "", ip: createRandomIp() }) {
super(params);

View File

@@ -122,6 +122,7 @@ export abstract class BaseServer implements IServer {
openPortCount?: number;
requiredHackingSkill?: number;
serverGrowth?: number;
isHacknetServer?: boolean;
constructor(params: IConstructorParams = { hostname: "", ip: createRandomIp() }) {
this.ip = params.ip ? params.ip : createRandomIp();
@@ -367,6 +368,6 @@ export abstract class BaseServer implements IServer {
// Customize a prune list for a subclass.
static getIncludedKeys<T extends BaseServer>(ctor: new () => T): readonly (keyof T)[] {
return getKeyList(ctor, { removedKeys: ["runningScriptMap", "savedScripts", "ramUsed"] });
return getKeyList(ctor, { removedKeys: ["runningScriptMap", "savedScripts", "ramUsed", "isHacknetServer"] });
}
}

View File

@@ -1,4 +1,6 @@
import type { WorkerScript } from "../../Netscript/WorkerScript";
import type { BaseServer } from "../../Server/BaseServer";
import React, { useState } from "react";
import { MenuItem, Typography, Select, SelectChangeEvent, TextField, IconButton, List } from "@mui/material";
@@ -8,9 +10,9 @@ import { ScriptProduction } from "./ScriptProduction";
import { ServerAccordion } from "./ServerAccordion";
import { workerScripts } from "../../Netscript/WorkerScripts";
import { getRecordEntries } from "../../Types/Record";
import { Settings } from "../../Settings/Settings";
import { isPositiveInteger } from "../../types";
import { SpecialServers } from "../../Server/data/SpecialServers";
export function ActiveScriptsPage(): React.ReactElement {
const [scriptsPerPage, setScriptsPerPage] = useState(Settings.ActiveScriptsScriptPageSize);
@@ -31,24 +33,55 @@ export function ActiveScriptsPage(): React.ReactElement {
setServersPerPage(n);
}
const serverData: [string, WorkerScript[]][] = (() => {
const tempData: Record<string, WorkerScript[]> = {};
// Creating and sorting the server data array is done here
const serverData: [BaseServer, WorkerScript[]][] = (() => {
const tempData: Map<BaseServer, WorkerScript[]> = new Map();
if (filter) {
// Only check filtering if a filter exists (performance)
for (const ws of workerScripts.values()) {
if (!ws.hostname.includes(filter) && !ws.scriptRef.filename.includes(filter)) continue;
const hostname = ws.hostname;
if (tempData[hostname]) tempData[hostname].push(ws);
else tempData[hostname] = [ws];
const server = ws.getServer();
const serverScripts = tempData.get(server);
if (serverScripts) serverScripts.push(ws);
else tempData.set(server, [ws]);
}
} else {
for (const ws of workerScripts.values()) {
const hostname = ws.hostname;
if (tempData[hostname]) tempData[hostname].push(ws);
else tempData[hostname] = [ws];
const server = ws.getServer();
const serverScripts = tempData.get(server);
if (serverScripts) serverScripts.push(ws);
else tempData.set(server, [ws]);
}
}
return getRecordEntries(tempData);
// serverData will be based on a sorted array from the temporary Map
return [...tempData].sort(([serverA], [serverB]) => {
// Servers not owned by the player are equal for sorting. Earliest return because it is the most common comparison.
if (!serverA.purchasedByPlayer && !serverB.purchasedByPlayer) return 0;
// Servers owned by the player come earlier in the sorting
if (serverA.purchasedByPlayer && !serverB.purchasedByPlayer) return -1;
if (!serverA.purchasedByPlayer && serverB.purchasedByPlayer) return 1;
// If we have reached this point, then both servers are player owned
// Home is at the top
if (serverA.hostname === SpecialServers.Home) return -1;
if (serverB.hostname === SpecialServers.Home) return 1;
// Hacknet servers shown after home
if (serverA.isHacknetServer && !serverB.isHacknetServer) return -1;
if (!serverA.isHacknetServer && serverB.isHacknetServer) return 1;
// Sorting for hacknet servers is based on the numbered suffix
if (serverA.isHacknetServer) {
if (serverA.hostname.length < serverB.hostname.length) return -1;
if (serverA.hostname.length > serverB.hostname.length) return 1;
// Get the numbered suffix from the end of the server names
const numA = Math.abs(parseInt(serverA.hostname.slice(-2)));
const numB = Math.abs(parseInt(serverB.hostname.slice(-2)));
if (numA < numB) return -1;
return 1;
}
// Sorting for other purchased servers is alphabetical. There's probably a better way to do this.
const fakeArray = [serverA.hostname, serverB.hostname].sort();
if (serverA.hostname === fakeArray[0]) return -1;
return 1;
});
})();
const lastPage = Math.max(Math.ceil(serverData.length / serversPerPage) - 1, 0);
@@ -112,8 +145,8 @@ export function ActiveScriptsPage(): React.ReactElement {
</IconButton>
</div>
<List dense={true}>
{dataToShow.map(([hostname, scripts]) => (
<ServerAccordion key={hostname} hostname={hostname} scripts={scripts} />
{dataToShow.map(([server, scripts]) => (
<ServerAccordion key={server.hostname} server={server} scripts={scripts} />
))}
</List>
</>

View File

@@ -1,45 +1,29 @@
/**
* React Component for rendering the Accordion element for a single
* server in the 'Active Scripts' UI page
*/
import type { WorkerScript } from "../../Netscript/WorkerScript";
import type { BaseServer } from "../../Server/BaseServer";
import * as React from "react";
import Typography from "@mui/material/Typography";
import { Box, Collapse, ListItemText, ListItemButton, Paper, Typography } from "@mui/material";
import { ExpandMore, ExpandLess } from "@mui/icons-material";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Paper from "@mui/material/Paper";
import Box from "@mui/material/Box";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
import { ServerAccordionContent } from "./ServerAccordionContent";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { GetServer } from "../../Server/AllServers";
interface ServerAccordionProps {
hostname: string;
server: BaseServer;
scripts: WorkerScript[];
}
export function ServerAccordion({ hostname, scripts }: ServerAccordionProps): React.ReactElement {
export function ServerAccordion({ server, scripts }: ServerAccordionProps): React.ReactElement {
const [open, setOpen] = React.useState(false);
const server = GetServer(hostname);
if (!server) {
console.error(`Invalid server ${hostname} while displaying active scripts`);
return <></>;
}
// Accordion's header text
// TODO: calculate the longest hostname length rather than hard coding it
const longestHostnameLength = 18;
const paddedName = `${hostname}${" ".repeat(longestHostnameLength)}`.slice(
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(
0,
Math.max(hostname.length, longestHostnameLength),
Math.max(server.hostname.length, longestHostnameLength),
);
const barOptions = {
progress: server.ramUsed / server.maxRam,