diff --git a/css/activescripts.scss b/css/activescripts.scss
index 82b7ae7b9..8dbfbc2e3 100644
--- a/css/activescripts.scss
+++ b/css/activescripts.scss
@@ -13,6 +13,12 @@
margin: 6px;
padding: 4px;
}
+
+ .accordion-header {
+ > pre {
+ color: white;
+ }
+ }
}
.active-scripts-server-header {
@@ -26,31 +32,32 @@
text-align: left;
border: none;
outline: none;
+
+ &:after {
+ content: '\02795'; /* "plus" sign (+) */
+ font-size: $defaultFontSize * 0.8125;
+ color: #fff;
+ float: right;
+ margin-left: 5px;
+ }
+
+ &.active, &:hover {
+ background-color: #555;
+ }
}
-.active-scripts-server-header.active,
-.active-scripts-server-header:hover {
- background-color: #555;
-}
+.active-scripts-server-header.active {
+ &:after {
+ content: "\2796"; /* "minus" sign (-) */
+ font-size: $defaultFontSize * 0.8125;
+ color: #fff;
+ float: right;
+ margin-left: 5px;
+ }
-.active-scripts-server-header.active:hover {
- background-color: #666;
-}
-
-.active-scripts-server-header:after {
- content: '\02795'; /* "plus" sign (+) */
- font-size: $defaultFontSize * 0.8125;
- color: #fff;
- float: right;
- margin-left: 5px;
-}
-
-.active-scripts-server-header.active:after {
- content: "\2796"; /* "minus" sign (-) */
- font-size: $defaultFontSize * 0.8125;
- color: #fff;
- float: right;
- margin-left: 5px;
+ &:hover {
+ background-color: #666;
+ }
}
.active-scripts-server-panel {
@@ -59,24 +66,23 @@
width: 55%;
margin-left: 5%;
display: none;
-}
-.active-scripts-server-panel div,
-.active-scripts-server-panel ul,
-.active-scripts-server-panel ul > li {
- background-color: #555;
+ div, ul, ul > li {
+ background-color: #555;
+ }
}
.active-scripts-script-header {
background-color: #555;
- color: var(--my-font-color);
- padding: 4px 25px 4px 10px;
- cursor: pointer;
- width: auto;
- text-align: left;
border: none;
+ color: var(--my-font-color);
+ cursor: pointer;
+ display: block;
outline: none;
+ padding: 4px 25px 4px 10px;
position: relative;
+ text-align: left;
+ width: auto;
&:after {
content: '\02795'; /* "plus" sign (+) */
@@ -104,13 +110,14 @@
}
.active-scripts-script-panel {
- padding: 0 18px;
background-color: #555;
- width: auto;
display: none;
+ font-size: 14px;
margin-bottom: 6px;
+ padding: 0 18px;
+ width: auto;
- p, h2, ul, li {
+ pre, h2, ul, li {
background-color: #555;
width: auto;
color: #fff;
diff --git a/css/styles.scss b/css/styles.scss
index 563a343de..febdf156a 100644
--- a/css/styles.scss
+++ b/css/styles.scss
@@ -243,8 +243,8 @@ a:visited {
/* Accordion menus (Header with collapsible panel) */
.accordion-header {
background-color: #444;
- font-size: $defaultFontSize * 1.25;
color: #fff;
+ font-size: $defaultFontSize * 1.25;
margin: 6px 6px 0 6px;
padding: 4px 6px;
cursor: pointer;
diff --git a/src/Netscript/WorkerScriptStartStopEventEmitter.ts b/src/Netscript/WorkerScriptStartStopEventEmitter.ts
new file mode 100644
index 000000000..8fdb922b8
--- /dev/null
+++ b/src/Netscript/WorkerScriptStartStopEventEmitter.ts
@@ -0,0 +1,6 @@
+/**
+ * Event emitter that triggers when scripts are started/stopped
+ */
+import { EventEmitter } from "../utils/EventEmitter";
+
+export const WorkerScriptStartStopEventEmitter = new EventEmitter();
diff --git a/src/Netscript/killWorkerScript.ts b/src/Netscript/killWorkerScript.ts
index 4c8c3561d..59abd4e93 100644
--- a/src/Netscript/killWorkerScript.ts
+++ b/src/Netscript/killWorkerScript.ts
@@ -4,6 +4,7 @@
*/
import { WorkerScript } from "./WorkerScript";
import { workerScripts } from "./WorkerScripts";
+import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
import { RunningScript } from "../Script/RunningScript";
import { AllServers } from "../Server/AllServers";
@@ -106,6 +107,7 @@ function removeWorkerScript(id: WorkerScript | number): void {
// Delete script from global pool (workerScripts)
workerScripts.splice(index, 1);
+ WorkerScriptStartStopEventEmitter.emitEvent();
}
/**
diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js
index 38da9c654..319693358 100644
--- a/src/NetscriptWorker.js
+++ b/src/NetscriptWorker.js
@@ -5,6 +5,7 @@
import { killWorkerScript } from "./Netscript/killWorkerScript";
import { WorkerScript } from "./Netscript/WorkerScript";
import { workerScripts } from "./Netscript/WorkerScripts";
+import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
@@ -25,7 +26,6 @@ import {
} from "./Script/ScriptHelpers";
import { AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
-import { EventEmitter } from "./utils/EventEmitter";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { generate } from "escodegen";
@@ -45,9 +45,6 @@ for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
NetscriptPorts.push(new NetscriptPort());
}
-// WorkerScript-related event emitter. Used for the UI
-export const WorkerScriptEventEmitter = new EventEmitter();
-
export function prestigeWorkerScripts() {
for (var i = 0; i < workerScripts.length; ++i) {
// TODO Signal event emitter
@@ -138,7 +135,7 @@ function startNetscript2Script(workerScript) {
}
function startNetscript1Script(workerScript) {
- var code = workerScript.code;
+ const code = workerScript.code;
workerScript.running = true;
//Process imports
@@ -448,9 +445,9 @@ export function addWorkerScript(runningScriptObj, server) {
// Start the script's execution
let p = null; // Script's resulting promise
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
- p = startNetscript2Script(workerScripts[i]);
+ p = startNetscript2Script(s);
} else {
- p = startNetscript1Script(workerScripts[i]);
+ p = startNetscript1Script(s);
if (!(p instanceof Promise)) { return; }
}
@@ -488,6 +485,7 @@ export function addWorkerScript(runningScriptObj, server) {
w.scriptRef.log("Script crashed with runtime error");
} else {
w.scriptRef.log("Script killed");
+ return; // Already killed, so stop here
}
w.running = false;
w.env.stopFlag = true;
@@ -505,6 +503,7 @@ export function addWorkerScript(runningScriptObj, server) {
// Add the WorkerScript to the global pool
workerScripts.push(s);
+ WorkerScriptStartStopEventEmitter.emitEvent();
return;
}
diff --git a/src/Script/RunningScript.ts b/src/Script/RunningScript.ts
index be26ddba8..eca819276 100644
--- a/src/Script/RunningScript.ts
+++ b/src/Script/RunningScript.ts
@@ -1,5 +1,7 @@
-// Class representing a Script instance that is actively running.
-// A Script can have multiple active instances
+/**
+ * Class representing a Script instance that is actively running.
+ * A Script can have multiple active instances
+ */
import { Script } from "./Script";
import { FconfSettings } from "../Fconf/FconfSettings";
import { Settings } from "../Settings/Settings";
@@ -22,10 +24,8 @@ export class RunningScript {
// Script arguments
args: any[] = [];
- // Holds a map of servers hacked, where server = key and the value for each
- // server is an array of four numbers. The four numbers represent:
- // [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
- // This data is used for offline progress
+ // Map of [key: server ip] -> Hacking data. Used for offline progress calculations.
+ // Hacking data format: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
dataMap: IMap = {};
// Script filename
diff --git a/src/Script/Script.ts b/src/Script/Script.ts
index f700b0ef1..cb3200a6c 100644
--- a/src/Script/Script.ts
+++ b/src/Script/Script.ts
@@ -1,6 +1,9 @@
-// Class representing a script file
-// This does NOT represent a script that is actively running and
-// being evaluated. See RunningScript for that
+/**
+ * Class representing a script file.
+ *
+ * This does NOT represent a script that is actively running and
+ * being evaluated. See RunningScript for that
+ */
import { calculateRamUsage } from "./RamCalculations";
import { Page, routing } from "../ui/navigationTracking";
@@ -34,7 +37,6 @@ export class Script {
// IP of server that this script is on.
server: string = "";
-
constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) {
this.filename = fn;
this.code = code;
@@ -44,6 +46,9 @@ export class Script {
if (this.code !== "") { this.updateRamUsage(otherScripts); }
};
+ /**
+ * Download the script as a file
+ */
download(): void {
const filename = this.filename + ".js";
const file = new Blob([this.code], {type: 'text/plain'});
@@ -63,10 +68,14 @@ export class Script {
}
}
- // Save a script FROM THE SCRIPT EDITOR
+ /**
+ * Save a script from the script editor
+ * @param {string} code - The new contents of the script
+ * @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
+ */
saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
if (routing.isOn(Page.ScriptEditor)) {
- //Update code and filename
+ // Update code and filename
this.code = code.replace(/^\s+|\s+$/g, '');
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
@@ -75,18 +84,16 @@ export class Script {
return;
}
this.filename = filenameElem!.value;
-
- // Server
this.server = serverIp;
-
- //Calculate/update ram usage, execution time, etc.
this.updateRamUsage(otherScripts);
-
this.module = "";
}
}
- // Updates the script's RAM usage based on its code
+ /**
+ * Calculates and updates the script's RAM usage based on its code
+ * @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
+ */
async updateRamUsage(otherScripts: Script[]) {
var res = await calculateRamUsage(this.code, otherScripts);
if (res > 0) {
diff --git a/src/engine.jsx b/src/engine.jsx
index 9ed84ce4f..20f2ff35b 100644
--- a/src/engine.jsx
+++ b/src/engine.jsx
@@ -45,6 +45,7 @@ import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions";
+import { workerScripts } from "./Netscript/WorkerScripts";
import {
loadAllRunningScripts,
updateOnlineScriptTimes,
@@ -90,6 +91,8 @@ import { displayCharacterInfo } from "./ui/displayCharacterInfo";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { setSettingsLabels } from "./ui/setSettingsLabels";
+
+import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
@@ -262,7 +265,10 @@ const Engine = {
Engine.hideAllContent();
Engine.Display.activeScriptsContent.style.display = "block";
routing.navigateTo(Page.ActiveScripts);
- updateActiveScriptsItems();
+ ReactDOM.render(
+ ,
+ Engine.Display.activeScriptsContent
+ )
MainMenuLinks.ActiveScripts.classList.add("active");
},
@@ -474,7 +480,10 @@ const Engine = {
Engine.Display.terminalContent.style.display = "none";
Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent.style.display = "none";
+
Engine.Display.activeScriptsContent.style.display = "none";
+ ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
+
clearHacknetNodesUI();
Engine.Display.createProgramContent.style.display = "none";
@@ -805,13 +814,14 @@ const Engine = {
}
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
- // Always update, but make the interval longer if the page isn't active
- updateActiveScriptsItems();
if (routing.isOn(Page.ActiveScripts)) {
- Engine.Counters.updateActiveScriptsDisplay = 5;
- } else {
- Engine.Counters.updateActiveScriptsDisplay = 10;
+ ReactDOM.render(
+ ,
+ Engine.Display.activeScriptsContent
+ )
}
+
+ Engine.Counters.updateActiveScriptsDisplay = 5;
}
if (Engine.Counters.updateDisplays <= 0) {
diff --git a/src/ui/ActiveScripts/Root.tsx b/src/ui/ActiveScripts/Root.tsx
index 193363017..2dd6e78d0 100644
--- a/src/ui/ActiveScripts/Root.tsx
+++ b/src/ui/ActiveScripts/Root.tsx
@@ -15,7 +15,7 @@ type IProps = {
workerScripts: WorkerScript[];
}
-export class ActiveScriptsRoot extends React.Component {
+export class ActiveScriptsRoot extends React.Component {
constructor(props: IProps) {
super(props);
}
diff --git a/src/ui/ActiveScripts/ScriptProduction.tsx b/src/ui/ActiveScripts/ScriptProduction.tsx
index a348b82cb..66da02a01 100644
--- a/src/ui/ActiveScripts/ScriptProduction.tsx
+++ b/src/ui/ActiveScripts/ScriptProduction.tsx
@@ -35,6 +35,7 @@ export function ScriptProduction(props: IProps): React.ReactElement {
{numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)}
+
(
{numeralWrapper.formatMoney(prodRateSinceLastAug)}
diff --git a/src/ui/ActiveScripts/ServerAccordion.tsx b/src/ui/ActiveScripts/ServerAccordion.tsx
index 9f49a88f3..9a0d1c57f 100644
--- a/src/ui/ActiveScripts/ServerAccordion.tsx
+++ b/src/ui/ActiveScripts/ServerAccordion.tsx
@@ -28,7 +28,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
progress: server.ramUsed / server.maxRam,
totalTicks: 30
};
- const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, ' ');
+ const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
const scripts = props.workerScripts.map((ws) => {
return (
@@ -39,7 +39,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
return (
{headerTxt}
+ {headerTxt}
}
panelContent={
diff --git a/src/ui/ActiveScripts/ServerAccordions.tsx b/src/ui/ActiveScripts/ServerAccordions.tsx
index 1a0dbcd9c..554d69317 100644
--- a/src/ui/ActiveScripts/ServerAccordions.tsx
+++ b/src/ui/ActiveScripts/ServerAccordions.tsx
@@ -6,9 +6,10 @@ import * as React from "react";
import { ServerAccordion } from "./ServerAccordion";
+import { WorkerScript } from "../../Netscript/WorkerScript";
+import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
import { getServer } from "../../Server/ServerHelpers";
import { BaseServer } from "../../Server/BaseServer";
-import { WorkerScript } from "../../Netscript/WorkerScript";
// Map of server hostname -> all workerscripts on that server for all active scripts
interface IServerData {
@@ -24,17 +25,37 @@ type IProps = {
workerScripts: WorkerScript[];
};
-export class ServerAccordions extends React.Component {
+type IState = {
+ rerenderFlag: boolean;
+}
+
+
+const subscriberId = "ActiveScriptsUI";
+
+export class ServerAccordions extends React.Component {
serverToScriptMap: IServerToScriptsMap = {};
constructor(props: IProps) {
super(props);
+ this.state = {
+ rerenderFlag: false,
+ }
+
this.updateServerToScriptsMap();
- // TODO
- // We subscribe to an event emitter that publishes whenever a script is
- // started/stopped. This allows us to only update the map when necessary
+ this.rerender = this.rerender.bind(this);
+ }
+
+ componentDidMount() {
+ WorkerScriptStartStopEventEmitter.addSubscriber({
+ cb: this.rerender,
+ id: subscriberId,
+ })
+ }
+
+ componentWillUnmount() {
+ WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
}
updateServerToScriptsMap(): void {
@@ -60,6 +81,13 @@ export class ServerAccordions extends React.Component {
this.serverToScriptMap = map;
}
+ rerender() {
+ this.updateServerToScriptsMap();
+ this.setState((prevState) => {
+ return { rerenderFlag: !prevState.rerenderFlag }
+ });
+ }
+
render() {
const elems = Object.keys(this.serverToScriptMap).map((serverName) => {
const data = this.serverToScriptMap[serverName];
diff --git a/src/ui/ActiveScripts/WorkerScriptAccordion.tsx b/src/ui/ActiveScripts/WorkerScriptAccordion.tsx
index 2f346220b..bbf522596 100644
--- a/src/ui/ActiveScripts/WorkerScriptAccordion.tsx
+++ b/src/ui/ActiveScripts/WorkerScriptAccordion.tsx
@@ -12,6 +12,7 @@ import { AccordionButton } from "../React/AccordionButton";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScript } from "../../Netscript/WorkerScript";
+import { dialogBoxCreate } from "../../../utils/DialogBox";
import { logBoxCreate } from "../../../utils/LogBox";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { arrayToString } from "../../../utils/helpers/arrayToString";
@@ -24,8 +25,14 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const workerScript = props.workerScript;
const scriptRef = workerScript.scriptRef;
+
const logClickHandler = logBoxCreate.bind(null, scriptRef);
- const killScriptButton = killWorkerScript.bind(null, scriptRef, scriptRef.server);
+ const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
+
+ function killScriptClickHandler() {
+ killScript();
+ dialogBoxCreate("Killing script");
+ }
// Calculations for script stats
const onlineMps = scriptRef.onlineMoneyMade / scriptRef.onlineRunningTime;
@@ -37,31 +44,30 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
- >
+ <>{props.workerScript.name}>
}
panelClass="active-scripts-script-panel"
panelContent={
<>
- Threads: {props.workerScript.scriptRef.threads}
- Args: {arrayToString(props.workerScript.args)}
- Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}
- Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}
- Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}
- {(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp").replace( / /g, " ")}
- Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second
- {(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ")}
- Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}
- {(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ")}
- Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second
- {(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, " ")}
+ Threads: {props.workerScript.scriptRef.threads}
+ Args: {arrayToString(props.workerScript.args)}
+ Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}
+ Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}
+ Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}
+ {(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp")}
+ Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second
+ {(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second")}
+ Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}
+ {(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp")}
+ Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second
+ {(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second")}
>
diff --git a/src/ui/React/Accordion.tsx b/src/ui/React/Accordion.tsx
index 948039076..a90be06f0 100644
--- a/src/ui/React/Accordion.tsx
+++ b/src/ui/React/Accordion.tsx
@@ -9,6 +9,7 @@ type IProps = {
panelClass?: string; // Override default class
panelContent: React.ReactElement;
panelInitiallyOpened?: boolean;
+ style?: string;
}
type IState = {
diff --git a/utils/LogBox.ts b/utils/LogBox.ts
index c22542958..1a38244c6 100644
--- a/utils/LogBox.ts
+++ b/utils/LogBox.ts
@@ -23,7 +23,6 @@ function logBoxInit(): void {
console.error(`Could not find LogBox's close button`);
return;
}
- logBoxClose();
closeButton.addEventListener("click", function() {
logBoxClose();
@@ -40,6 +39,8 @@ function logBoxInit(): void {
logBoxContainer = document.getElementById("log-box-container");
logText = document.getElementById("log-box-text");
+ logBoxClose();
+
document.removeEventListener("DOMContentLoaded", logBoxInit);
};