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={
    {scripts}
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); };