mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-30 03:23:48 +01:00
Merge pull request #614 from danielyxie/active-scripts-ui-and-implementation-rework
Active scripts ui and implementation rework
This commit is contained in:
commit
8a00e6e532
126
css/activescripts.scss
Normal file
126
css/activescripts.scss
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
@import "theme";
|
||||||
|
|
||||||
|
.active-scripts-list {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#active-scripts-container {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
width: 70%;
|
||||||
|
margin: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header {
|
||||||
|
> pre {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-server-header {
|
||||||
|
background-color: #444;
|
||||||
|
font-size: $defaultFontSize * 1.25;
|
||||||
|
color: #fff;
|
||||||
|
margin: 6px 6px 0 6px;
|
||||||
|
padding: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 60%;
|
||||||
|
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 {
|
||||||
|
&: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 {
|
||||||
|
margin: 0 6px 6px 6px;
|
||||||
|
padding: 0 6px 6px 6px;
|
||||||
|
width: 55%;
|
||||||
|
margin-left: 5%;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
div, ul, ul > li {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-script-header {
|
||||||
|
background-color: #555;
|
||||||
|
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 (+) */
|
||||||
|
font-size: $defaultFontSize * 0.8125;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: 0 0 0 var(--my-font-color);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active:after {
|
||||||
|
content: "\2796"; /* "minus" sign (-) */
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active:hover {
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-scripts-script-panel {
|
||||||
|
background-color: #555;
|
||||||
|
display: none;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding: 0 18px;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
pre, h2, ul, li {
|
||||||
|
background-color: #555;
|
||||||
|
width: auto;
|
||||||
|
color: #fff;
|
||||||
|
margin-left: 5%;
|
||||||
|
}
|
||||||
|
}
|
@ -18,126 +18,6 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Active scripts */
|
|
||||||
.active-scripts-list {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#active-scripts-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#active-scripts-text,
|
|
||||||
#active-scripts-total-prod {
|
|
||||||
width: 70%;
|
|
||||||
margin: 6px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header {
|
|
||||||
background-color: #444;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
color: #fff;
|
|
||||||
margin: 6px 6px 0 6px;
|
|
||||||
padding: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 60%;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-header.active,
|
|
||||||
.active-scripts-server-header:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-server-panel {
|
|
||||||
margin: 0 6px 6px 6px;
|
|
||||||
padding: 0 6px 6px 6px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
outline: none;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '\02795'; /* "plus" sign (+) */
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
float: right;
|
|
||||||
margin-left: 5px;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 0 var(--my-font-color);
|
|
||||||
position: absolute;
|
|
||||||
bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active:after {
|
|
||||||
content: "\2796"; /* "minus" sign (-) */
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&.active:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-scripts-script-panel {
|
|
||||||
padding: 0 18px;
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
display: none;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
|
|
||||||
p, h2, ul, li {
|
|
||||||
background-color: #555;
|
|
||||||
width: auto;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* World */
|
/* World */
|
||||||
#world-container {
|
#world-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -243,8 +243,8 @@ a:visited {
|
|||||||
/* Accordion menus (Header with collapsible panel) */
|
/* Accordion menus (Header with collapsible panel) */
|
||||||
.accordion-header {
|
.accordion-header {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-size: $defaultFontSize * 1.25;
|
||||||
margin: 6px 6px 0 6px;
|
margin: 6px 6px 0 6px;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -1,337 +0,0 @@
|
|||||||
// TODO - Convert this to React
|
|
||||||
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
|
|
||||||
import { Player } from "./Player";
|
|
||||||
import { getServer } from "./Server/ServerHelpers";
|
|
||||||
|
|
||||||
import { Page, routing } from "./ui/navigationTracking";
|
|
||||||
import { numeralWrapper } from "./ui/numeralFormat";
|
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
|
||||||
import { logBoxCreate } from "../utils/LogBox";
|
|
||||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
|
||||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
|
||||||
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
|
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
|
||||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
|
||||||
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
|
||||||
import { getElementById } from "../utils/uiHelpers/getElementById";
|
|
||||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
|
||||||
import { removeElement } from "../utils/uiHelpers/removeElement";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {
|
|
||||||
* serverName: {
|
|
||||||
* header: Server Header Element
|
|
||||||
* panel: Server Panel List (ul) element
|
|
||||||
* scripts: {
|
|
||||||
* script id: Ref to Script information
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
*/
|
|
||||||
const ActiveScriptsUI = {};
|
|
||||||
const ActiveScriptsTasks = []; // Sequentially schedule the creation/deletion of UI elements
|
|
||||||
|
|
||||||
const getHeaderHtml = (server) => {
|
|
||||||
// TODO: calculate the longest hostname length rather than hard coding it
|
|
||||||
const longestHostnameLength = 18;
|
|
||||||
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
|
|
||||||
const barOptions = {
|
|
||||||
progress: server.ramUsed / server.maxRam,
|
|
||||||
totalTicks: 30
|
|
||||||
};
|
|
||||||
return `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateHeaderHtml = (server) => {
|
|
||||||
const accordion = ActiveScriptsUI[server.hostname];
|
|
||||||
if (accordion === null || accordion === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert it to a string, as that's how it's stored it will come out of the data attributes
|
|
||||||
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
|
|
||||||
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
|
||||||
accordion.header.dataset.ramPercentage = ramPercentage;
|
|
||||||
accordion.header.innerHTML = getHeaderHtml(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createActiveScriptsServerPanel(server) {
|
|
||||||
let hostname = server.hostname;
|
|
||||||
|
|
||||||
var activeScriptsList = document.getElementById("active-scripts-list");
|
|
||||||
|
|
||||||
let res = createAccordionElement({
|
|
||||||
hdrText: getHeaderHtml(server)
|
|
||||||
});
|
|
||||||
let li = res[0];
|
|
||||||
var hdr = res[1];
|
|
||||||
let panel = res[2];
|
|
||||||
|
|
||||||
if (ActiveScriptsUI[hostname] != null) {
|
|
||||||
console.log("WARNING: Tried to create already-existing Active Scripts Server panel. This is most likely fine. It probably means many scripts just got started up on a new server. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var panelScriptList = createElement("ul");
|
|
||||||
panel.appendChild(panelScriptList);
|
|
||||||
activeScriptsList.appendChild(li);
|
|
||||||
|
|
||||||
ActiveScriptsUI[hostname] = {
|
|
||||||
header: hdr,
|
|
||||||
panel: panel,
|
|
||||||
panelList: panelScriptList,
|
|
||||||
scripts: {}, // Holds references to li elements for each active script
|
|
||||||
scriptHdrs: {}, // Holds references to header elements for each active script
|
|
||||||
scriptStats: {}, // Holds references to the p elements containing text for each active script
|
|
||||||
};
|
|
||||||
|
|
||||||
return li;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the info for a particular server (Dropdown header + Panel with all info)
|
|
||||||
* in the Active Scripts page if it exists
|
|
||||||
*/
|
|
||||||
function deleteActiveScriptsServerPanel(server) {
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
console.log("WARNING: Tried to delete non-existent Active Scripts Server panel. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it's empty
|
|
||||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
|
|
||||||
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeElement(ActiveScriptsUI[hostname].panel);
|
|
||||||
removeElement(ActiveScriptsUI[hostname].header);
|
|
||||||
delete ActiveScriptsUI[hostname];
|
|
||||||
}
|
|
||||||
|
|
||||||
function addActiveScriptsItem(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
|
|
||||||
ActiveScriptsTasks.push(function(workerscript, hostname) {
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
createActiveScriptsServerPanel(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the unique identifier (key) for this script
|
|
||||||
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
let res = createAccordionElement({hdrText:workerscript.name});
|
|
||||||
let li = res[0];
|
|
||||||
let hdr = res[1];
|
|
||||||
let panel = res[2];
|
|
||||||
|
|
||||||
hdr.classList.remove("accordion-header");
|
|
||||||
hdr.classList.add("active-scripts-script-header");
|
|
||||||
panel.classList.remove("accordion-panel");
|
|
||||||
panel.classList.add("active-scripts-script-panel");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the constant elements on the panel that don't change after creation:
|
|
||||||
* Threads, args, kill/log button
|
|
||||||
*/
|
|
||||||
panel.appendChild(createElement("p", {
|
|
||||||
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
|
|
||||||
"Args: " + arrayToString(workerscript.args)
|
|
||||||
}));
|
|
||||||
var panelText = createElement("p", {
|
|
||||||
innerText: "Loading...",
|
|
||||||
fontSize: "14px",
|
|
||||||
});
|
|
||||||
panel.appendChild(panelText);
|
|
||||||
panel.appendChild(createElement("br"));
|
|
||||||
panel.appendChild(createElement("span", {
|
|
||||||
innerText: "Log",
|
|
||||||
class: "accordion-button",
|
|
||||||
margin: "4px",
|
|
||||||
padding: "4px",
|
|
||||||
clickListener: () => {
|
|
||||||
logBoxCreate(workerscript.scriptRef);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
panel.appendChild(createElement("span", {
|
|
||||||
innerText: "Kill Script",
|
|
||||||
class: "accordion-button",
|
|
||||||
margin: "4px",
|
|
||||||
padding: "4px",
|
|
||||||
clickListener: () => {
|
|
||||||
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.server);
|
|
||||||
dialogBoxCreate("Killing script, may take a few minutes to complete...");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Append element to list
|
|
||||||
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
|
||||||
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
|
||||||
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
|
||||||
ActiveScriptsUI[hostname].scriptStats[itemName] = panelText;
|
|
||||||
}.bind(null, workerscript, hostname));
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteActiveScriptsItem(workerscript) {
|
|
||||||
ActiveScriptsTasks.push(function(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
throw new Error("ERROR: Invalid server IP for workerscript. This most likely occurred because " +
|
|
||||||
"you tried to delete a large number of scripts and also deleted servers at the " +
|
|
||||||
"same time. It's not a big deal, just save and refresh the game.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
console.log("ERROR: Trying to delete Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
let li = ActiveScriptsUI[hostname].scripts[itemName];
|
|
||||||
if (li == null) {
|
|
||||||
console.log("ERROR: Cannot find Active Script UI element for workerscript: ");
|
|
||||||
console.log(workerscript);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
removeElement(li);
|
|
||||||
delete ActiveScriptsUI[hostname].scripts[itemName];
|
|
||||||
delete ActiveScriptsUI[hostname].scriptHdrs[itemName];
|
|
||||||
delete ActiveScriptsUI[hostname].scriptStats[itemName];
|
|
||||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length === 0) {
|
|
||||||
deleteActiveScriptsServerPanel(server);
|
|
||||||
}
|
|
||||||
}.bind(null, workerscript));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateActiveScriptsItems(maxTasks=150) {
|
|
||||||
/**
|
|
||||||
* Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
|
|
||||||
* We'll limit this to 150 at a time for performance (in case someone decides to start a
|
|
||||||
* bunch of scripts all at once...)
|
|
||||||
*/
|
|
||||||
const numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
|
||||||
for (let i = 0; i < numTasks; ++i) {
|
|
||||||
let task = ActiveScriptsTasks.shift();
|
|
||||||
try {
|
|
||||||
task();
|
|
||||||
} catch(e) {
|
|
||||||
exceptionAlert(e);
|
|
||||||
console.log(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let total = 0;
|
|
||||||
for (var i = 0; i < workerScripts.length; ++i) {
|
|
||||||
try {
|
|
||||||
total += updateActiveScriptsItemContent(workerScripts[i]);
|
|
||||||
} catch(e) {
|
|
||||||
exceptionAlert(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!routing.isOn(Page.ActiveScripts)) { return total; }
|
|
||||||
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
|
|
||||||
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
|
|
||||||
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateActiveScriptsItemContent(workerscript) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.log("ERROR: Invalid server IP for workerscript in updateActiveScriptsItemContent().");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null) {
|
|
||||||
return; // Hasn't been created yet. We'll skip it
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHeaderHtml(server);
|
|
||||||
|
|
||||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
|
||||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
|
||||||
itemNameArray.push(String(workerscript.args[i]));
|
|
||||||
}
|
|
||||||
var itemName = itemNameArray.join("-");
|
|
||||||
|
|
||||||
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
|
||||||
return; // Hasn't been fully added yet. We'll skip it
|
|
||||||
}
|
|
||||||
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
|
||||||
|
|
||||||
// Update the text if necessary. This fn returns the online $/s production
|
|
||||||
return updateActiveScriptsText(workerscript, item, itemName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateActiveScriptsText(workerscript, item, itemName) {
|
|
||||||
var server = getServer(workerscript.serverIp);
|
|
||||||
if (server == null) {
|
|
||||||
console.log("ERROR: Invalid server IP for workerscript for updateActiveScriptsText()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hostname = server.hostname;
|
|
||||||
if (ActiveScriptsUI[hostname] == null || ActiveScriptsUI[hostname].scriptHdrs[itemName] == null) {
|
|
||||||
console.log("ERROR: Trying to update Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHeaderHtml(server);
|
|
||||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
|
||||||
|
|
||||||
// Only update if the item is visible
|
|
||||||
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
|
||||||
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
|
||||||
|
|
||||||
removeChildrenFromElement(item);
|
|
||||||
|
|
||||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
|
||||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
|
||||||
|
|
||||||
// Online
|
|
||||||
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
|
|
||||||
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, " ");
|
|
||||||
|
|
||||||
var onlineMpsText = "Online production rate: " + numeralWrapper.formatMoney(onlineMps) + " / second";
|
|
||||||
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
|
||||||
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
|
||||||
|
|
||||||
// Offline
|
|
||||||
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
|
|
||||||
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ");
|
|
||||||
|
|
||||||
var offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
|
|
||||||
var offlineMpsText = "Offline production rate: " + numeralWrapper.formatMoney(offlineMps) + " / second";
|
|
||||||
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
|
|
||||||
var offlineEpsText = (Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, " ");
|
|
||||||
|
|
||||||
item.innerHTML = onlineTime + "<br>" + offlineTime + "<br>" + onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
|
|
||||||
onlineMpsText + "<br>" + onlineEpsText + "<br>" + offlineTotalMoneyMade + "<br>" + offlineTotalExpEarned + "<br>" +
|
|
||||||
offlineMpsText + "<br>" + offlineEpsText + "<br>";
|
|
||||||
return onlineMps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {addActiveScriptsItem, deleteActiveScriptsItem, updateActiveScriptsItems};
|
|
@ -32,6 +32,11 @@ export class WorkerScript {
|
|||||||
*/
|
*/
|
||||||
delay: number | null = null;
|
delay: number | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the Promise resolve() function for when the script is "blocked" by an async op
|
||||||
|
*/
|
||||||
|
delayResolve?: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores names of all functions that have logging disabled
|
* Stores names of all functions that have logging disabled
|
||||||
*/
|
*/
|
||||||
|
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Event emitter that triggers when scripts are started/stopped
|
||||||
|
*/
|
||||||
|
import { EventEmitter } from "../utils/EventEmitter";
|
||||||
|
|
||||||
|
export const WorkerScriptStartStopEventEmitter = new EventEmitter();
|
6
src/Netscript/WorkerScripts.ts
Normal file
6
src/Netscript/WorkerScripts.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Global pool of all active scripts (scripts that are currently running)
|
||||||
|
*/
|
||||||
|
import { WorkerScript } from "./WorkerScript";
|
||||||
|
|
||||||
|
export const workerScripts: WorkerScript[] = [];
|
127
src/Netscript/killWorkerScript.ts
Normal file
127
src/Netscript/killWorkerScript.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* Stops an actively-running script (represented by a WorkerScript object)
|
||||||
|
* and removes it from the global pool of active scripts.
|
||||||
|
*/
|
||||||
|
import { WorkerScript } from "./WorkerScript";
|
||||||
|
import { workerScripts } from "./WorkerScripts";
|
||||||
|
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
||||||
|
|
||||||
|
import { RunningScript } from "../Script/RunningScript";
|
||||||
|
import { AllServers } from "../Server/AllServers";
|
||||||
|
|
||||||
|
import { compareArrays } from "../../utils/helpers/compareArrays";
|
||||||
|
import { roundToTwo } from "../../utils/helpers/roundToTwo";
|
||||||
|
|
||||||
|
export function killWorkerScript(runningScriptObj: RunningScript, serverIp: string): boolean;
|
||||||
|
export function killWorkerScript(workerScript: WorkerScript): boolean;
|
||||||
|
export function killWorkerScript(script: RunningScript | WorkerScript, serverIp?: string): boolean {
|
||||||
|
if (script instanceof WorkerScript) {
|
||||||
|
script.env.stopFlag = true;
|
||||||
|
killNetscriptDelay(script);
|
||||||
|
removeWorkerScript(script);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (script instanceof RunningScript && typeof serverIp === "string") {
|
||||||
|
for (let i = 0; i < workerScripts.length; i++) {
|
||||||
|
if (workerScripts[i].name == script.filename && workerScripts[i].serverIp == serverIp &&
|
||||||
|
compareArrays(workerScripts[i].args, script.args)) {
|
||||||
|
workerScripts[i].env.stopFlag = true;
|
||||||
|
killNetscriptDelay(workerScripts[i]);
|
||||||
|
removeWorkerScript(workerScripts[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
console.error(`killWorkerScript() called with invalid argument:`);
|
||||||
|
console.error(script);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that removes the script being killed from the global pool.
|
||||||
|
* Also handles other cleanup-time operations
|
||||||
|
*
|
||||||
|
* @param {WorkerScript | number} - Identifier for WorkerScript. Either the object itself, or
|
||||||
|
* its index in the global workerScripts array
|
||||||
|
*/
|
||||||
|
function removeWorkerScript(id: WorkerScript | number): void {
|
||||||
|
// Get a reference to the WorkerScript and its index in the global pool
|
||||||
|
let workerScript: WorkerScript;
|
||||||
|
let index: number | null = null;
|
||||||
|
|
||||||
|
if (typeof id === "number") {
|
||||||
|
if (id < 0 || id >= workerScripts.length) {
|
||||||
|
console.error(`Too high of an index passed into removeWorkerScript(): ${id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
workerScript = workerScripts[id];
|
||||||
|
index = id;
|
||||||
|
} else if (id instanceof WorkerScript) {
|
||||||
|
workerScript = id;
|
||||||
|
for (let i = 0; i < workerScripts.length; ++i) {
|
||||||
|
if (workerScripts[i] == id) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == null) {
|
||||||
|
console.error(`Could not find WorkerScript in global pool:`);
|
||||||
|
console.error(workerScript);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`Invalid argument passed into removeWorkerScript(): ${id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ip = workerScript.serverIp;
|
||||||
|
const name = workerScript.name;
|
||||||
|
|
||||||
|
// Get the server on which the script runs
|
||||||
|
const server = AllServers[ip];
|
||||||
|
if (server == null) {
|
||||||
|
console.error(`Could not find server on which this script is running: ${ip}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate ram used on that server
|
||||||
|
server.ramUsed = roundToTwo(server.ramUsed - workerScript.ramUsage);
|
||||||
|
if (server.ramUsed < 0) {
|
||||||
|
console.warn(`Server RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`);
|
||||||
|
server.ramUsed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete script from global pool (workerScripts)
|
||||||
|
workerScripts.splice(<number>index, 1);
|
||||||
|
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that interrupts a script's delay if it is in the middle of a
|
||||||
|
* timed, blocked operation (like hack(), sleep(), etc.). This allows scripts to
|
||||||
|
* be killed immediately even if they're in the middle of one of those long operations
|
||||||
|
*/
|
||||||
|
function killNetscriptDelay(workerScript: WorkerScript) {
|
||||||
|
if (workerScript instanceof WorkerScript) {
|
||||||
|
if (workerScript.delay) {
|
||||||
|
clearTimeout(workerScript.delay);
|
||||||
|
if (workerScript.delayResolve) {
|
||||||
|
workerScript.delayResolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,9 @@
|
|||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
|
||||||
import { getServer } from "./Server/ServerHelpers";
|
|
||||||
|
|
||||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||||
import { parse, Node } from "../utils/acorn";
|
import { parse, Node } from "../utils/acorn";
|
||||||
|
|
||||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
||||||
import { isString } from "../utils/helpers/isString";
|
import { isString } from "../utils/helpers/isString";
|
||||||
|
|
||||||
export function killNetscriptDelay(workerScript) {
|
|
||||||
if (workerScript instanceof WorkerScript) {
|
|
||||||
if (workerScript.delay) {
|
|
||||||
clearTimeout(workerScript.delay);
|
|
||||||
workerScript.delayResolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function netscriptDelay(time, workerScript) {
|
export function netscriptDelay(time, workerScript) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
workerScript.delay = setTimeoutRef(() => {
|
workerScript.delay = setTimeoutRef(() => {
|
||||||
|
@ -3,7 +3,6 @@ const vsprintf = require("sprintf-js").vsprintf;
|
|||||||
|
|
||||||
import { getRamCost } from "./Netscript/RamCostGenerator";
|
import { getRamCost } from "./Netscript/RamCostGenerator";
|
||||||
|
|
||||||
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
|
|
||||||
import { Augmentation } from "./Augmentation/Augmentation";
|
import { Augmentation } from "./Augmentation/Augmentation";
|
||||||
import { Augmentations } from "./Augmentation/Augmentations";
|
import { Augmentations } from "./Augmentation/Augmentations";
|
||||||
import {
|
import {
|
||||||
@ -120,11 +119,11 @@ import {
|
|||||||
} from "./NetscriptBladeburner";
|
} from "./NetscriptBladeburner";
|
||||||
import * as nsGang from "./NetscriptGang";
|
import * as nsGang from "./NetscriptGang";
|
||||||
import {
|
import {
|
||||||
workerScripts,
|
|
||||||
killWorkerScript,
|
|
||||||
NetscriptPorts,
|
NetscriptPorts,
|
||||||
runScriptFromScript,
|
runScriptFromScript,
|
||||||
} from "./NetscriptWorker";
|
} from "./NetscriptWorker";
|
||||||
|
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||||
|
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||||
import {
|
import {
|
||||||
makeRuntimeRejectMsg,
|
makeRuntimeRejectMsg,
|
||||||
netscriptDelay,
|
netscriptDelay,
|
||||||
|
@ -2,20 +2,17 @@
|
|||||||
* Functions for handling WorkerScripts, which are the underlying mechanism
|
* Functions for handling WorkerScripts, which are the underlying mechanism
|
||||||
* that allows for scripts to run
|
* that allows for scripts to run
|
||||||
*/
|
*/
|
||||||
|
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||||
|
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||||
|
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||||
|
|
||||||
import {
|
|
||||||
addActiveScriptsItem,
|
|
||||||
deleteActiveScriptsItem,
|
|
||||||
updateActiveScriptsItems
|
|
||||||
} from "./ActiveScriptsUI";
|
|
||||||
import { CONSTANTS } from "./Constants";
|
import { CONSTANTS } from "./Constants";
|
||||||
import { Engine } from "./engine";
|
import { Engine } from "./engine";
|
||||||
import { Interpreter } from "./JSInterpreter";
|
import { Interpreter } from "./JSInterpreter";
|
||||||
import {
|
import {
|
||||||
isScriptErrorMessage,
|
isScriptErrorMessage,
|
||||||
makeRuntimeRejectMsg,
|
makeRuntimeRejectMsg,
|
||||||
killNetscriptDelay
|
|
||||||
} from "./NetscriptEvaluator";
|
} from "./NetscriptEvaluator";
|
||||||
import { NetscriptFunctions } from "./NetscriptFunctions";
|
import { NetscriptFunctions } from "./NetscriptFunctions";
|
||||||
import { executeJSScript } from "./NetscriptJSEvaluator";
|
import { executeJSScript } from "./NetscriptJSEvaluator";
|
||||||
@ -42,9 +39,7 @@ import { isString } from "../utils/StringHelperFunctions";
|
|||||||
|
|
||||||
const walk = require("acorn/dist/walk");
|
const walk = require("acorn/dist/walk");
|
||||||
|
|
||||||
//Array containing all scripts that are running across all servers, to easily run them all
|
// Netscript Ports are instantiated here
|
||||||
export const workerScripts = [];
|
|
||||||
|
|
||||||
export const NetscriptPorts = [];
|
export const NetscriptPorts = [];
|
||||||
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
||||||
NetscriptPorts.push(new NetscriptPort());
|
NetscriptPorts.push(new NetscriptPort());
|
||||||
@ -52,10 +47,9 @@ for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
|||||||
|
|
||||||
export function prestigeWorkerScripts() {
|
export function prestigeWorkerScripts() {
|
||||||
for (var i = 0; i < workerScripts.length; ++i) {
|
for (var i = 0; i < workerScripts.length; ++i) {
|
||||||
deleteActiveScriptsItem(workerScripts[i]);
|
// TODO Signal event emitter
|
||||||
workerScripts[i].env.stopFlag = true;
|
workerScripts[i].env.stopFlag = true;
|
||||||
}
|
}
|
||||||
updateActiveScriptsItems(5000); //Force UI to update
|
|
||||||
workerScripts.length = 0;
|
workerScripts.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +135,7 @@ function startNetscript2Script(workerScript) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startNetscript1Script(workerScript) {
|
function startNetscript1Script(workerScript) {
|
||||||
var code = workerScript.code;
|
const code = workerScript.code;
|
||||||
workerScript.running = true;
|
workerScript.running = true;
|
||||||
|
|
||||||
//Process imports
|
//Process imports
|
||||||
@ -413,66 +407,55 @@ function processNetscript1Imports(code, workerScript) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through workerScripts and run every script that is not currently running
|
/**
|
||||||
export function runScriptsLoop() {
|
* Start a script
|
||||||
let scriptDeleted = false;
|
*
|
||||||
|
* Given a RunningScript object, constructs a corresponding WorkerScript,
|
||||||
|
* adds it to the global 'workerScripts' pool, and begins executing it.
|
||||||
|
* @param {RunningScript} runningScriptObj - Script that's being run
|
||||||
|
* @param {Server} server - Server on which the script is to be run
|
||||||
|
*/
|
||||||
|
export function addWorkerScript(runningScriptObj, server) {
|
||||||
|
const filename = runningScriptObj.filename;
|
||||||
|
|
||||||
// Delete any scripts that finished or have been killed. Loop backwards bc removing items screws up indexing
|
// Update server's ram usage
|
||||||
for (let i = workerScripts.length - 1; i >= 0; i--) {
|
let threads = 1;
|
||||||
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == true) {
|
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
|
||||||
scriptDeleted = true;
|
threads = runningScriptObj.threads;
|
||||||
// Delete script from the runningScripts array on its host serverIp
|
|
||||||
const ip = workerScripts[i].serverIp;
|
|
||||||
const name = workerScripts[i].name;
|
|
||||||
|
|
||||||
// Recalculate ram used
|
|
||||||
AllServers[ip].ramUsed = 0;
|
|
||||||
for (let j = 0; j < workerScripts.length; j++) {
|
|
||||||
if (workerScripts[j].serverIp !== ip) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (j === i) { // not this one
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
AllServers[ip].ramUsed += workerScripts[j].ramUsage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete script from Active Scripts
|
|
||||||
deleteActiveScriptsItem(workerScripts[i]);
|
|
||||||
|
|
||||||
for (let j = 0; j < AllServers[ip].runningScripts.length; j++) {
|
|
||||||
if (AllServers[ip].runningScripts[j].filename == name &&
|
|
||||||
compareArrays(AllServers[ip].runningScripts[j].args, workerScripts[i].args)) {
|
|
||||||
AllServers[ip].runningScripts.splice(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete script from workerScripts
|
|
||||||
workerScripts.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (scriptDeleted) { updateActiveScriptsItems(); } // Force Update
|
|
||||||
|
|
||||||
|
|
||||||
// Run any scripts that haven't been started
|
|
||||||
for (let i = 0; i < workerScripts.length; i++) {
|
|
||||||
// If it isn't running, start the script
|
|
||||||
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
|
|
||||||
let p = null; // p is the script's result promise.
|
|
||||||
if (workerScripts[i].name.endsWith(".js") || workerScripts[i].name.endsWith(".ns")) {
|
|
||||||
p = startNetscript2Script(workerScripts[i]);
|
|
||||||
} else {
|
} else {
|
||||||
p = startNetscript1Script(workerScripts[i]);
|
runningScriptObj.threads = 1;
|
||||||
if (!(p instanceof Promise)) { continue; }
|
}
|
||||||
|
const ramUsage = roundToTwo(getRamUsageFromRunningScript(runningScriptObj) * threads);
|
||||||
|
const ramAvailable = server.maxRam - server.ramUsed;
|
||||||
|
if (ramUsage > ramAvailable) {
|
||||||
|
dialogBoxCreate(
|
||||||
|
`Not enough RAM to run script ${runningScriptObj.filename} with args ` +
|
||||||
|
`${arrayToString(runningScriptObj.args)}. This likely occurred because you re-loaded ` +
|
||||||
|
`the game and the script's RAM usage increased (either because of an update to the game or ` +
|
||||||
|
`your changes to the script.)`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server.ramUsed = roundToTwo(server.ramUsed + ramUsage);
|
||||||
|
|
||||||
|
// Create the WorkerScript
|
||||||
|
const s = new WorkerScript(runningScriptObj, NetscriptFunctions);
|
||||||
|
s.ramUsage = ramUsage;
|
||||||
|
|
||||||
|
// Start the script's execution
|
||||||
|
let p = null; // Script's resulting promise
|
||||||
|
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
||||||
|
p = startNetscript2Script(s);
|
||||||
|
} else {
|
||||||
|
p = startNetscript1Script(s);
|
||||||
|
if (!(p instanceof Promise)) { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||||
// running status to false
|
// running status to false
|
||||||
p.then(function(w) {
|
p.then(function(w) {
|
||||||
console.log("Stopping script " + w.name + " because it finished running naturally");
|
console.log("Stopping script " + w.name + " because it finished running naturally");
|
||||||
w.running = false;
|
killWorkerScript(s);
|
||||||
w.env.stopFlag = true;
|
|
||||||
w.scriptRef.log("Script finished running");
|
w.scriptRef.log("Script finished running");
|
||||||
}).catch(function(w) {
|
}).catch(function(w) {
|
||||||
if (w instanceof Error) {
|
if (w instanceof Error) {
|
||||||
@ -502,6 +485,7 @@ export function runScriptsLoop() {
|
|||||||
w.scriptRef.log("Script crashed with runtime error");
|
w.scriptRef.log("Script crashed with runtime error");
|
||||||
} else {
|
} else {
|
||||||
w.scriptRef.log("Script killed");
|
w.scriptRef.log("Script killed");
|
||||||
|
return; // Already killed, so stop here
|
||||||
}
|
}
|
||||||
w.running = false;
|
w.running = false;
|
||||||
w.env.stopFlag = true;
|
w.env.stopFlag = true;
|
||||||
@ -513,64 +497,13 @@ export function runScriptsLoop() {
|
|||||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||||
console.log(w);
|
console.log(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
killWorkerScript(s);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeoutRef(runScriptsLoop, 3e3);
|
// Add the WorkerScript to the global pool
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues a script to be killed by setting its stop flag to true. This
|
|
||||||
* kills and timed/blocking Netscript functions (like hack(), sleep(), etc.) and
|
|
||||||
* prevents any further execution of Netscript functions.
|
|
||||||
* The runScriptsLoop() handles the actual deletion of the WorkerScript
|
|
||||||
*/
|
|
||||||
export function killWorkerScript(runningScriptObj, serverIp) {
|
|
||||||
for (var i = 0; i < workerScripts.length; i++) {
|
|
||||||
if (workerScripts[i].name == runningScriptObj.filename && workerScripts[i].serverIp == serverIp &&
|
|
||||||
compareArrays(workerScripts[i].args, runningScriptObj.args)) {
|
|
||||||
workerScripts[i].env.stopFlag = true;
|
|
||||||
killNetscriptDelay(workerScripts[i]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a RunningScript object, queues that script to be run
|
|
||||||
*/
|
|
||||||
export function addWorkerScript(runningScriptObj, server) {
|
|
||||||
var filename = runningScriptObj.filename;
|
|
||||||
|
|
||||||
//Update server's ram usage
|
|
||||||
var threads = 1;
|
|
||||||
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
|
|
||||||
threads = runningScriptObj.threads;
|
|
||||||
} else {
|
|
||||||
runningScriptObj.threads = 1;
|
|
||||||
}
|
|
||||||
var ramUsage = roundToTwo(getRamUsageFromRunningScript(runningScriptObj) * threads);
|
|
||||||
var ramAvailable = server.maxRam - server.ramUsed;
|
|
||||||
if (ramUsage > ramAvailable) {
|
|
||||||
dialogBoxCreate("Not enough RAM to run script " + runningScriptObj.filename + " with args " +
|
|
||||||
arrayToString(runningScriptObj.args) + ". This likely occurred because you re-loaded " +
|
|
||||||
"the game and the script's RAM usage increased (either because of an update to the game or " +
|
|
||||||
"your changes to the script.)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
server.ramUsed = roundToTwo(server.ramUsed + ramUsage);
|
|
||||||
|
|
||||||
//Create the WorkerScript
|
|
||||||
var s = new WorkerScript(runningScriptObj, NetscriptFunctions);
|
|
||||||
s.ramUsage = ramUsage;
|
|
||||||
|
|
||||||
//Add the WorkerScript to the Active Scripts list
|
|
||||||
addActiveScriptsItem(s);
|
|
||||||
|
|
||||||
//Add the WorkerScript
|
|
||||||
workerScripts.push(s);
|
workerScripts.push(s);
|
||||||
|
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { deleteActiveScriptsItem } from "./ActiveScriptsUI";
|
|
||||||
import { Augmentations } from "./Augmentation/Augmentations";
|
import { Augmentations } from "./Augmentation/Augmentations";
|
||||||
import {
|
import {
|
||||||
augmentationExists,
|
augmentationExists,
|
||||||
|
@ -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 { Script } from "./Script";
|
||||||
import { FconfSettings } from "../Fconf/FconfSettings";
|
import { FconfSettings } from "../Fconf/FconfSettings";
|
||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
@ -22,10 +24,8 @@ export class RunningScript {
|
|||||||
// Script arguments
|
// Script arguments
|
||||||
args: any[] = [];
|
args: any[] = [];
|
||||||
|
|
||||||
// Holds a map of servers hacked, where server = key and the value for each
|
// Map of [key: server ip] -> Hacking data. Used for offline progress calculations.
|
||||||
// server is an array of four numbers. The four numbers represent:
|
// Hacking data format: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||||
// [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
|
||||||
// This data is used for offline progress
|
|
||||||
dataMap: IMap<number[]> = {};
|
dataMap: IMap<number[]> = {};
|
||||||
|
|
||||||
// Script filename
|
// Script filename
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// Class representing a script file
|
/**
|
||||||
// This does NOT represent a script that is actively running and
|
* Class representing a script file.
|
||||||
// being evaluated. See RunningScript for that
|
*
|
||||||
|
* This does NOT represent a script that is actively running and
|
||||||
|
* being evaluated. See RunningScript for that
|
||||||
|
*/
|
||||||
import { calculateRamUsage } from "./RamCalculations";
|
import { calculateRamUsage } from "./RamCalculations";
|
||||||
import { Page, routing } from "../ui/navigationTracking";
|
import { Page, routing } from "../ui/navigationTracking";
|
||||||
|
|
||||||
@ -34,7 +37,6 @@ export class Script {
|
|||||||
// IP of server that this script is on.
|
// IP of server that this script is on.
|
||||||
server: string = "";
|
server: string = "";
|
||||||
|
|
||||||
|
|
||||||
constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) {
|
constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) {
|
||||||
this.filename = fn;
|
this.filename = fn;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
@ -44,6 +46,9 @@ export class Script {
|
|||||||
if (this.code !== "") { this.updateRamUsage(otherScripts); }
|
if (this.code !== "") { this.updateRamUsage(otherScripts); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download the script as a file
|
||||||
|
*/
|
||||||
download(): void {
|
download(): void {
|
||||||
const filename = this.filename + ".js";
|
const filename = this.filename + ".js";
|
||||||
const file = new Blob([this.code], {type: 'text/plain'});
|
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 {
|
saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
|
||||||
if (routing.isOn(Page.ScriptEditor)) {
|
if (routing.isOn(Page.ScriptEditor)) {
|
||||||
//Update code and filename
|
// Update code and filename
|
||||||
this.code = code.replace(/^\s+|\s+$/g, '');
|
this.code = code.replace(/^\s+|\s+$/g, '');
|
||||||
|
|
||||||
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
|
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
|
||||||
@ -75,18 +84,16 @@ export class Script {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.filename = filenameElem!.value;
|
this.filename = filenameElem!.value;
|
||||||
|
|
||||||
// Server
|
|
||||||
this.server = serverIp;
|
this.server = serverIp;
|
||||||
|
|
||||||
//Calculate/update ram usage, execution time, etc.
|
|
||||||
this.updateRamUsage(otherScripts);
|
this.updateRamUsage(otherScripts);
|
||||||
|
|
||||||
this.module = "";
|
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[]) {
|
async updateRamUsage(otherScripts: Script[]) {
|
||||||
var res = await calculateRamUsage(this.code, otherScripts);
|
var res = await calculateRamUsage(this.code, otherScripts);
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
|
@ -53,7 +53,8 @@ import {
|
|||||||
import { showLiterature } from "./Literature";
|
import { showLiterature } from "./Literature";
|
||||||
import { Message } from "./Message/Message";
|
import { Message } from "./Message/Message";
|
||||||
import { showMessage } from "./Message/MessageHelpers";
|
import { showMessage } from "./Message/MessageHelpers";
|
||||||
import { killWorkerScript, addWorkerScript } from "./NetscriptWorker";
|
import { addWorkerScript } from "./NetscriptWorker";
|
||||||
|
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { hackWorldDaemon } from "./RedPill";
|
import { hackWorldDaemon } from "./RedPill";
|
||||||
import { RunningScript } from "./Script/RunningScript";
|
import { RunningScript } from "./Script/RunningScript";
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Game engine. Handles the main game loop as well as the main UI pages
|
||||||
|
*
|
||||||
|
* TODO: Separate UI functionality into its own component
|
||||||
|
*/
|
||||||
import {
|
import {
|
||||||
convertTimeMsToTimeElapsedString,
|
convertTimeMsToTimeElapsedString,
|
||||||
replaceAt
|
replaceAt
|
||||||
} from "../utils/StringHelperFunctions";
|
} from "../utils/StringHelperFunctions";
|
||||||
import { logBoxUpdateText, logBoxOpened } from "../utils/LogBox";
|
import { logBoxUpdateText, logBoxOpened } from "../utils/LogBox";
|
||||||
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
|
|
||||||
import { Augmentations } from "./Augmentation/Augmentations";
|
import { Augmentations } from "./Augmentation/Augmentations";
|
||||||
import {
|
import {
|
||||||
initAugmentations,
|
initAugmentations,
|
||||||
@ -41,9 +45,9 @@ import { LocationName } from "./Locations/data/LocationNames";
|
|||||||
import { LocationRoot } from "./Locations/ui/Root";
|
import { LocationRoot } from "./Locations/ui/Root";
|
||||||
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
|
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
|
||||||
import { inMission, currMission } from "./Missions";
|
import { inMission, currMission } from "./Missions";
|
||||||
|
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||||
import {
|
import {
|
||||||
loadAllRunningScripts,
|
loadAllRunningScripts,
|
||||||
runScriptsLoop,
|
|
||||||
updateOnlineScriptTimes,
|
updateOnlineScriptTimes,
|
||||||
} from "./NetscriptWorker";
|
} from "./NetscriptWorker";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
@ -87,6 +91,8 @@ import { displayCharacterInfo } from "./ui/displayCharacterInfo";
|
|||||||
import { Page, routing } from "./ui/navigationTracking";
|
import { Page, routing } from "./ui/navigationTracking";
|
||||||
import { numeralWrapper } from "./ui/numeralFormat";
|
import { numeralWrapper } from "./ui/numeralFormat";
|
||||||
import { setSettingsLabels } from "./ui/setSettingsLabels";
|
import { setSettingsLabels } from "./ui/setSettingsLabels";
|
||||||
|
|
||||||
|
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
|
||||||
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
||||||
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
||||||
|
|
||||||
@ -259,7 +265,10 @@ const Engine = {
|
|||||||
Engine.hideAllContent();
|
Engine.hideAllContent();
|
||||||
Engine.Display.activeScriptsContent.style.display = "block";
|
Engine.Display.activeScriptsContent.style.display = "block";
|
||||||
routing.navigateTo(Page.ActiveScripts);
|
routing.navigateTo(Page.ActiveScripts);
|
||||||
updateActiveScriptsItems();
|
ReactDOM.render(
|
||||||
|
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
|
||||||
|
Engine.Display.activeScriptsContent
|
||||||
|
)
|
||||||
MainMenuLinks.ActiveScripts.classList.add("active");
|
MainMenuLinks.ActiveScripts.classList.add("active");
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -471,7 +480,10 @@ const Engine = {
|
|||||||
Engine.Display.terminalContent.style.display = "none";
|
Engine.Display.terminalContent.style.display = "none";
|
||||||
Engine.Display.characterContent.style.display = "none";
|
Engine.Display.characterContent.style.display = "none";
|
||||||
Engine.Display.scriptEditorContent.style.display = "none";
|
Engine.Display.scriptEditorContent.style.display = "none";
|
||||||
|
|
||||||
Engine.Display.activeScriptsContent.style.display = "none";
|
Engine.Display.activeScriptsContent.style.display = "none";
|
||||||
|
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
|
||||||
|
|
||||||
clearHacknetNodesUI();
|
clearHacknetNodesUI();
|
||||||
Engine.Display.createProgramContent.style.display = "none";
|
Engine.Display.createProgramContent.style.display = "none";
|
||||||
|
|
||||||
@ -802,13 +814,14 @@ const Engine = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
|
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
|
||||||
// Always update, but make the interval longer if the page isn't active
|
|
||||||
updateActiveScriptsItems();
|
|
||||||
if (routing.isOn(Page.ActiveScripts)) {
|
if (routing.isOn(Page.ActiveScripts)) {
|
||||||
Engine.Counters.updateActiveScriptsDisplay = 5;
|
ReactDOM.render(
|
||||||
} else {
|
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
|
||||||
Engine.Counters.updateActiveScriptsDisplay = 10;
|
Engine.Display.activeScriptsContent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Engine.Counters.updateActiveScriptsDisplay = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Engine.Counters.updateDisplays <= 0) {
|
if (Engine.Counters.updateDisplays <= 0) {
|
||||||
@ -1515,9 +1528,6 @@ const Engine = {
|
|||||||
start: function() {
|
start: function() {
|
||||||
// Run main loop
|
// Run main loop
|
||||||
Engine.idleTimer();
|
Engine.idleTimer();
|
||||||
|
|
||||||
// Script-processing loop
|
|
||||||
runScriptsLoop();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import "../css/characteroverview.scss";
|
|||||||
import "../css/terminal.scss";
|
import "../css/terminal.scss";
|
||||||
import "../css/scripteditor.scss";
|
import "../css/scripteditor.scss";
|
||||||
import "../css/codemirror-overrides.scss";
|
import "../css/codemirror-overrides.scss";
|
||||||
|
import "../css/activescripts.scss";
|
||||||
import "../css/hacknetnodes.scss";
|
import "../css/hacknetnodes.scss";
|
||||||
import "../css/menupages.scss";
|
import "../css/menupages.scss";
|
||||||
import "../css/augmentations.scss";
|
import "../css/augmentations.scss";
|
||||||
|
38
src/ui/ActiveScripts/Root.tsx
Normal file
38
src/ui/ActiveScripts/Root.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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 * as React from "react";
|
||||||
|
|
||||||
|
import { ScriptProduction } from "./ScriptProduction";
|
||||||
|
import { ServerAccordions } from "./ServerAccordions";
|
||||||
|
|
||||||
|
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
p: IPlayer;
|
||||||
|
workerScripts: WorkerScript[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActiveScriptsRoot extends React.Component<IProps> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ScriptProduction {...this.props} />
|
||||||
|
<ServerAccordions {...this.props} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
46
src/ui/ActiveScripts/ScriptProduction.tsx
Normal file
46
src/ui/ActiveScripts/ScriptProduction.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* React Component for displaying the total production and production rate
|
||||||
|
* of scripts on the 'Active Scripts' UI page
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { numeralWrapper } from "../numeralFormat";
|
||||||
|
|
||||||
|
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
p: IPlayer;
|
||||||
|
workerScripts: WorkerScript[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ScriptProduction(props: IProps): React.ReactElement {
|
||||||
|
const prodRateSinceLastAug = props.p.scriptProdSinceLastAug / (props.p.playtimeSinceLastAug / 1000);
|
||||||
|
|
||||||
|
let onlineProduction = 0;
|
||||||
|
for (const ws of props.workerScripts) {
|
||||||
|
onlineProduction += (ws.scriptRef.onlineMoneyMade / ws.scriptRef.onlineRunningTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p id="active-scripts-total-prod">
|
||||||
|
Total online production of Active scripts:
|
||||||
|
<span className="money-gold">
|
||||||
|
<span id="active-scripts-total-production-active">
|
||||||
|
{numeralWrapper.formatMoney(onlineProduction)}
|
||||||
|
</span> / sec
|
||||||
|
</span><br />
|
||||||
|
|
||||||
|
Total online production since last Aug installation:
|
||||||
|
<span id="active-scripts-total-prod-aug-total" className="money-gold">
|
||||||
|
{numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
(<span className="money-gold">
|
||||||
|
<span id="active-scripts-total-prod-aug-avg" className="money-gold">
|
||||||
|
{numeralWrapper.formatMoney(prodRateSinceLastAug)}
|
||||||
|
</span> / sec
|
||||||
|
</span>)
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
49
src/ui/ActiveScripts/ServerAccordion.tsx
Normal file
49
src/ui/ActiveScripts/ServerAccordion.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* React Component for rendering the Accordion element for a single
|
||||||
|
* server in the 'Active Scripts' UI page
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||||
|
import { Accordion } from "../React/Accordion";
|
||||||
|
|
||||||
|
import { BaseServer } from "../../Server/BaseServer";
|
||||||
|
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||||
|
|
||||||
|
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
server: BaseServer;
|
||||||
|
workerScripts: WorkerScript[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ServerAccordion(props: IProps): React.ReactElement {
|
||||||
|
const server = props.server;
|
||||||
|
|
||||||
|
// Accordion's header text
|
||||||
|
// TODO: calculate the longest hostname length rather than hard coding it
|
||||||
|
const longestHostnameLength = 18;
|
||||||
|
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
|
||||||
|
const barOptions = {
|
||||||
|
progress: server.ramUsed / server.maxRam,
|
||||||
|
totalTicks: 30
|
||||||
|
};
|
||||||
|
const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
|
||||||
|
|
||||||
|
const scripts = props.workerScripts.map((ws) => {
|
||||||
|
return (
|
||||||
|
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Accordion
|
||||||
|
headerContent={
|
||||||
|
<pre>{headerTxt}</pre>
|
||||||
|
}
|
||||||
|
panelContent={
|
||||||
|
<ul>{scripts}</ul>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
109
src/ui/ActiveScripts/ServerAccordions.tsx
Normal file
109
src/ui/ActiveScripts/ServerAccordions.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* React Component for rendering the Accordion elements for all servers
|
||||||
|
* on which scripts are running
|
||||||
|
*/
|
||||||
|
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";
|
||||||
|
|
||||||
|
// Map of server hostname -> all workerscripts on that server for all active scripts
|
||||||
|
interface IServerData {
|
||||||
|
server: BaseServer;
|
||||||
|
workerScripts: WorkerScript[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IServerToScriptsMap {
|
||||||
|
[key: string]: IServerData;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
workerScripts: WorkerScript[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const subscriberId = "ActiveScriptsUI";
|
||||||
|
|
||||||
|
export class ServerAccordions extends React.Component<IProps, IState> {
|
||||||
|
serverToScriptMap: IServerToScriptsMap = {};
|
||||||
|
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateServerToScriptsMap();
|
||||||
|
|
||||||
|
this.rerender = this.rerender.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
WorkerScriptStartStopEventEmitter.addSubscriber({
|
||||||
|
cb: this.rerender,
|
||||||
|
id: subscriberId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateServerToScriptsMap(): void {
|
||||||
|
const map: IServerToScriptsMap = {};
|
||||||
|
|
||||||
|
for (const ws of this.props.workerScripts) {
|
||||||
|
const server = getServer(ws.serverIp);
|
||||||
|
if (server == null) {
|
||||||
|
console.warn(`WorkerScript has invalid IP address: ${ws.serverIp}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map[server.hostname] == null) {
|
||||||
|
map[server.hostname] = {
|
||||||
|
server: server,
|
||||||
|
workerScripts: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
map[server.hostname].workerScripts.push(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
return (
|
||||||
|
<ServerAccordion
|
||||||
|
key={serverName}
|
||||||
|
server={data.server}
|
||||||
|
workerScripts={data.workerScripts}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="active-scripts-list" id="active-scripts-list">
|
||||||
|
{elems}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
77
src/ui/ActiveScripts/WorkerScriptAccordion.tsx
Normal file
77
src/ui/ActiveScripts/WorkerScriptAccordion.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* React Component for displaying a single WorkerScript's info as an
|
||||||
|
* Accordion element
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { numeralWrapper } from "../numeralFormat";
|
||||||
|
|
||||||
|
import { Accordion } from "../React/Accordion";
|
||||||
|
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";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
workerScript: WorkerScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WorkerScriptAccordion(props: IProps): React.ReactElement {
|
||||||
|
const workerScript = props.workerScript;
|
||||||
|
const scriptRef = workerScript.scriptRef;
|
||||||
|
|
||||||
|
|
||||||
|
const logClickHandler = logBoxCreate.bind(null, scriptRef);
|
||||||
|
const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
|
||||||
|
|
||||||
|
function killScriptClickHandler() {
|
||||||
|
killScript();
|
||||||
|
dialogBoxCreate("Killing script");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculations for script stats
|
||||||
|
const onlineMps = scriptRef.onlineMoneyMade / scriptRef.onlineRunningTime;
|
||||||
|
const onlineEps = scriptRef.onlineExpGained / scriptRef.onlineRunningTime;
|
||||||
|
const offlineMps = scriptRef.offlineMoneyMade / scriptRef.offlineRunningTime;
|
||||||
|
const offlineEps = scriptRef.offlineExpGained / scriptRef.offlineRunningTime;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Accordion
|
||||||
|
headerClass="active-scripts-script-header"
|
||||||
|
headerContent={
|
||||||
|
<>{props.workerScript.name}</>
|
||||||
|
}
|
||||||
|
panelClass="active-scripts-script-panel"
|
||||||
|
panelContent={
|
||||||
|
<>
|
||||||
|
<pre>Threads: {props.workerScript.scriptRef.threads}</pre>
|
||||||
|
<pre>Args: {arrayToString(props.workerScript.args)}</pre>
|
||||||
|
<pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
|
||||||
|
<pre>Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}</pre>
|
||||||
|
<pre>Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}</pre>
|
||||||
|
<pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp")}</pre>
|
||||||
|
<pre>Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second</pre>
|
||||||
|
<pre>{(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second")}</pre>
|
||||||
|
<pre>Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}</pre>
|
||||||
|
<pre>{(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp")}</pre>
|
||||||
|
<pre>Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second</pre>
|
||||||
|
<pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second")}</pre>
|
||||||
|
|
||||||
|
<AccordionButton
|
||||||
|
onClick={logClickHandler}
|
||||||
|
text="Log"
|
||||||
|
/>
|
||||||
|
<AccordionButton
|
||||||
|
onClick={killScriptClickHandler}
|
||||||
|
text="Kill Script"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -4,9 +4,12 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
|
headerClass?: string; // Override default class
|
||||||
headerContent: React.ReactElement;
|
headerContent: React.ReactElement;
|
||||||
|
panelClass?: string; // Override default class
|
||||||
panelContent: React.ReactElement;
|
panelContent: React.ReactElement;
|
||||||
panelInitiallyOpened?: boolean;
|
panelInitiallyOpened?: boolean;
|
||||||
|
style?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IState = {
|
type IState = {
|
||||||
@ -44,12 +47,21 @@ export class Accordion extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let className = "accordion-header";
|
||||||
|
if (typeof this.props.headerClass === "string") {
|
||||||
|
className = this.props.headerClass;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button className={"accordion-header"} onClick={this.handleHeaderClick}>
|
<button className={className} onClick={this.handleHeaderClick}>
|
||||||
{this.props.headerContent}
|
{this.props.headerContent}
|
||||||
</button>
|
</button>
|
||||||
<AccordionPanel opened={this.state.panelOpened} panelContent={this.props.panelContent} />
|
<AccordionPanel
|
||||||
|
opened={this.state.panelOpened}
|
||||||
|
panelClass={this.props.panelClass}
|
||||||
|
panelContent={this.props.panelContent}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -57,6 +69,7 @@ export class Accordion extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
type IPanelProps = {
|
type IPanelProps = {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
|
panelClass?: string; // Override default class
|
||||||
panelContent: React.ReactElement;
|
panelContent: React.ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +79,13 @@ class AccordionPanel extends React.Component<IPanelProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let className = "accordion-panel"
|
||||||
|
if (typeof this.props.panelClass === "string") {
|
||||||
|
className = this.props.panelClass;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"accordion-panel"}>
|
<div className={className}>
|
||||||
{this.props.panelContent}
|
{this.props.panelContent}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
52
src/ui/React/AccordionButton.tsx
Normal file
52
src/ui/React/AccordionButton.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Basic stateless button that uses the 'accordion-button' css class.
|
||||||
|
* This class has a black background so that it does not clash with the default
|
||||||
|
* accordion coloring
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
addClasses?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
id?: string;
|
||||||
|
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||||
|
style?: object;
|
||||||
|
text: string;
|
||||||
|
tooltip?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IInnerHTMLMarkup = {
|
||||||
|
__html: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccordionButton(props: IProps): React.ReactElement {
|
||||||
|
const hasTooltip = props.tooltip != null && props.tooltip !== "";
|
||||||
|
|
||||||
|
// TODO Add a disabled class for accordion buttons?
|
||||||
|
let className = "accordion-button";
|
||||||
|
if (hasTooltip) {
|
||||||
|
className += " tooltip";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.addClasses === "string") {
|
||||||
|
className += ` ${props.addClasses}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tooltip will be set using inner HTML
|
||||||
|
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||||
|
if (hasTooltip) {
|
||||||
|
tooltipMarkup = {
|
||||||
|
__html: props.tooltip!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={className} id={props.id} onClick={props.onClick} style={props.style}>
|
||||||
|
{props.text}
|
||||||
|
{
|
||||||
|
hasTooltip &&
|
||||||
|
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
50
src/utils/EventEmitter.ts
Normal file
50
src/utils/EventEmitter.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Generic Event Emitter class following a subscribe/publish paradigm.
|
||||||
|
*/
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
type cbFn = (...args: any[]) => any;
|
||||||
|
|
||||||
|
export interface ISubscriber {
|
||||||
|
/**
|
||||||
|
* Callback function that will be run when an event is emitted
|
||||||
|
*/
|
||||||
|
cb: cbFn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name/identifier for this subscriber
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventEmitter {
|
||||||
|
/**
|
||||||
|
* Map of Subscriber name -> Callback function
|
||||||
|
*/
|
||||||
|
subscribers: IMap<cbFn> = {};
|
||||||
|
|
||||||
|
constructor(subs?: ISubscriber[]) {
|
||||||
|
if (Array.isArray(subs)) {
|
||||||
|
for (const s of subs) {
|
||||||
|
this.addSubscriber(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubscriber(s: ISubscriber) {
|
||||||
|
this.subscribers[s.id] = s.cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitEvent(...args: any[]): void {
|
||||||
|
for (const s in this.subscribers) {
|
||||||
|
const cb = this.subscribers[s];
|
||||||
|
|
||||||
|
cb(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSubscriber(id: string) {
|
||||||
|
delete this.subscribers[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
import {killWorkerScript} from "../src/NetscriptWorker";
|
|
||||||
import {clearEventListeners} from "./uiHelpers/clearEventListeners";
|
|
||||||
import {arrayToString} from "./helpers/arrayToString";
|
|
||||||
|
|
||||||
$(document).keydown(function(event) {
|
|
||||||
if (logBoxOpened && event.keyCode == 27) {
|
|
||||||
logBoxClose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function logBoxInit() {
|
|
||||||
var closeButton = document.getElementById("log-box-close");
|
|
||||||
logBoxClose();
|
|
||||||
|
|
||||||
//Close Dialog box
|
|
||||||
closeButton.addEventListener("click", function() {
|
|
||||||
logBoxClose();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
document.getElementById("log-box-text-header").style.display = "inline-block";
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", logBoxInit, false);
|
|
||||||
|
|
||||||
function logBoxClose() {
|
|
||||||
logBoxOpened = false;
|
|
||||||
var logBox = document.getElementById("log-box-container");
|
|
||||||
logBox.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function logBoxOpen() {
|
|
||||||
logBoxOpened = true;
|
|
||||||
|
|
||||||
var logBox = document.getElementById("log-box-container");
|
|
||||||
logBox.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var logBoxOpened = false;
|
|
||||||
var logBoxCurrentScript = null;
|
|
||||||
function logBoxCreate(script) {
|
|
||||||
logBoxCurrentScript = script;
|
|
||||||
var killScriptBtn = clearEventListeners("log-box-kill-script");
|
|
||||||
killScriptBtn.addEventListener("click", ()=>{
|
|
||||||
killWorkerScript(script, script.server);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
document.getElementById('log-box-kill-script').style.display = "inline-block";
|
|
||||||
logBoxOpen();
|
|
||||||
document.getElementById("log-box-text-header").innerHTML =
|
|
||||||
logBoxCurrentScript.filename + " " + arrayToString(logBoxCurrentScript.args) + ":<br><br>";
|
|
||||||
logBoxCurrentScript.logUpd = true;
|
|
||||||
logBoxUpdateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
function logBoxUpdateText() {
|
|
||||||
var txt = document.getElementById("log-box-text");
|
|
||||||
if (logBoxCurrentScript && logBoxOpened && txt && logBoxCurrentScript.logUpd) {
|
|
||||||
txt.innerHTML = "";
|
|
||||||
for (var i = 0; i < logBoxCurrentScript.logs.length; ++i) {
|
|
||||||
txt.innerHTML += logBoxCurrentScript.logs[i];
|
|
||||||
txt.innerHTML += "<br>";
|
|
||||||
}
|
|
||||||
logBoxCurrentScript.logUpd = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {logBoxCreate, logBoxUpdateText, logBoxOpened, logBoxCurrentScript};
|
|
106
utils/LogBox.ts
Normal file
106
utils/LogBox.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { killWorkerScript } from "../src/Netscript/killWorkerScript";
|
||||||
|
import { RunningScript } from "../src/Script/RunningScript";
|
||||||
|
|
||||||
|
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
|
||||||
|
import { arrayToString } from "./helpers/arrayToString";
|
||||||
|
|
||||||
|
import { KEY } from "./helpers/keyCodes";
|
||||||
|
|
||||||
|
document.addEventListener("keydown", function(event: KeyboardEvent) {
|
||||||
|
if (logBoxOpened && event.keyCode == KEY.ESC) {
|
||||||
|
logBoxClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let logBoxContainer: HTMLElement | null;
|
||||||
|
let textHeader: HTMLElement | null;
|
||||||
|
let logText: HTMLElement | null;
|
||||||
|
|
||||||
|
function logBoxInit(): void {
|
||||||
|
// Initialize Close button click listener
|
||||||
|
const closeButton = document.getElementById("log-box-close");
|
||||||
|
if (closeButton == null) {
|
||||||
|
console.error(`Could not find LogBox's close button`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeButton.addEventListener("click", function() {
|
||||||
|
logBoxClose();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize text header
|
||||||
|
textHeader = document.getElementById("log-box-text-header");
|
||||||
|
if (textHeader instanceof HTMLElement) {
|
||||||
|
textHeader.style.display = "inline-block";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize references to other DOM elements
|
||||||
|
logBoxContainer = document.getElementById("log-box-container");
|
||||||
|
logText = document.getElementById("log-box-text");
|
||||||
|
|
||||||
|
logBoxClose();
|
||||||
|
|
||||||
|
document.removeEventListener("DOMContentLoaded", logBoxInit);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", logBoxInit);
|
||||||
|
|
||||||
|
function logBoxClose() {
|
||||||
|
logBoxOpened = false;
|
||||||
|
if (logBoxContainer instanceof HTMLElement) {
|
||||||
|
logBoxContainer.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logBoxOpen() {
|
||||||
|
logBoxOpened = true;
|
||||||
|
|
||||||
|
if (logBoxContainer instanceof HTMLElement) {
|
||||||
|
logBoxContainer.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export let logBoxOpened = false;
|
||||||
|
let logBoxCurrentScript: RunningScript | null = null;
|
||||||
|
export function logBoxCreate(script: RunningScript) {
|
||||||
|
logBoxCurrentScript = script;
|
||||||
|
|
||||||
|
const killScriptBtn = clearEventListeners("log-box-kill-script");
|
||||||
|
if (killScriptBtn == null) {
|
||||||
|
console.error(`Could not find LogBox's 'Kill Script' button`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
killScriptBtn.addEventListener("click", () => {
|
||||||
|
killWorkerScript(script, script.server);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
killScriptBtn.style.display = "inline-block";
|
||||||
|
|
||||||
|
logBoxOpen();
|
||||||
|
|
||||||
|
if (textHeader instanceof HTMLElement) {
|
||||||
|
textHeader.innerHTML = `${logBoxCurrentScript.filename} ${arrayToString(logBoxCurrentScript.args)}:<br><br>`;
|
||||||
|
} else {
|
||||||
|
console.warn(`LogBox's Text Header DOM element is null`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logBoxCurrentScript.logUpd = true;
|
||||||
|
logBoxUpdateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logBoxUpdateText() {
|
||||||
|
if (!(logText instanceof HTMLElement)) { return; }
|
||||||
|
|
||||||
|
if (logBoxCurrentScript && logBoxOpened && logBoxCurrentScript.logUpd) {
|
||||||
|
logText.innerHTML = "";
|
||||||
|
for (let i = 0; i < logBoxCurrentScript.logs.length; ++i) {
|
||||||
|
logText.innerHTML += logBoxCurrentScript.logs[i];
|
||||||
|
logText.innerHTML += "<br>";
|
||||||
|
}
|
||||||
|
logBoxCurrentScript.logUpd = false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user