Refactored stock buying/selling code into its own file. Refactored WorkerScript & NetscriptEnvironment into their own Typescript classes. Refactored a ton of code to remove circular dependencies

This commit is contained in:
danielyxie
2019-05-04 21:03:40 -07:00
parent 585e1ac7aa
commit 8a5b6f6cbc
47 changed files with 2867 additions and 2773 deletions
+31 -108
View File
@@ -1,11 +1,8 @@
// Calculate a script's RAM usage
const walk = require("acorn/dist/walk"); // Importing this doesn't work for some reason.
import { CONSTANTS } from "../Constants";
import {evaluateImport} from "../NetscriptEvaluator";
import { WorkerScript } from "../NetscriptWorker";
import { Player } from "../Player";
import {parse, Node} from "../../utils/acorn";
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
import { parse, Node } from "../../utils/acorn";
// These special strings are used to reference the presence of a given logical
// construct within a user script.
@@ -90,7 +87,7 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
// Finally, walk the reference map and generate a ram cost. The initial set of keys to scan
// are those that start with __SPECIAL_INITIAL_MODULE__.
let ram = CONSTANTS.ScriptBaseRamCost;
let ram = RamCostConstants.ScriptBaseRamCost;
const unresolvedRefs = Object.keys(dependencyMap).filter(s => s.startsWith(initialModule));
const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) {
@@ -98,13 +95,13 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
// Check if this is one of the special keys, and add the appropriate ram cost if so.
if (ref === "hacknet" && !resolvedRefs.has("hacknet")) {
ram += CONSTANTS.ScriptHacknetNodesRamCost;
ram += RamCostConstants.ScriptHacknetNodesRamCost;
}
if (ref === "document" && !resolvedRefs.has("document")) {
ram += CONSTANTS.ScriptDomRamCost;
ram += RamCostConstants.ScriptDomRamCost;
}
if (ref === "window" && !resolvedRefs.has("window")) {
ram += CONSTANTS.ScriptDomRamCost;
ram += RamCostConstants.ScriptDomRamCost;
}
resolvedRefs.add(ref);
@@ -124,9 +121,8 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
}
}
// Check if this ident is a function in the workerscript env. If it is, then we need to
// get its RAM cost. We do this by calling it, which works because the running script
// is in checkingRam mode.
// Check if this identifier is a function in the workerscript env.
// If it is, then we need to get its RAM cost.
//
// TODO it would be simpler to just reference a dictionary.
try {
@@ -152,8 +148,15 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
}
}
//Special logic for namespaces (Bladeburner, CodingCOntract)
var func;
// Only count each function once
if (workerScript.loadedFns[ref]) {
continue;
} else {
workerScript.loadedFns[ref] = true;
}
// This accounts for namespaces (Bladeburner, CodingCOntract)
let func;
if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref];
} else if (ref in workerScript.env.vars.codingcontract) {
@@ -163,7 +166,7 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
} else if (ref in workerScript.env.vars.sleeve) {
func = workerScript.env.vars.sleeve[ref];
} else {
func = workerScript.env.get(ref);
func = workerScript.env.vars[ref];
}
ram += applyFuncRam(func);
} catch (error) {continue;}
@@ -309,103 +312,23 @@ function parseOnlyCalculateDeps(code, currentModule) {
}
export async function calculateRamUsage(codeCopy) {
//Create a temporary/mock WorkerScript and an AST from the code
var currServ = Player.getCurrentServer();
var workerScript = new WorkerScript({
filename:"foo",
scriptRef: {code:""},
args:[],
getCode: function() { return ""; }
});
workerScript.checkingRam = true; //Netscript functions will return RAM usage
workerScript.serverIp = currServ.ip;
// We don't need a real WorkerScript for this. Just an object that keeps
// track of whatever's needed for RAM calculations
const workerScript = {
loadedFns: {},
serverIp: currServ.ip,
env: {
vars: RamCosts,
}
}
try {
return await parseOnlyRamCalculate(currServ, codeCopy, workerScript);
} catch (e) {
console.log("Failed to parse ram using new method. Falling back.", e);
console.error(`Failed to parse script for RAM calculations:`);
console.error(e);
return -1;
}
// Try the old way.
try {
var ast = parse(codeCopy, {sourceType:"module"});
} catch(e) {
return -1;
}
//Search through AST, scanning for any 'Identifier' nodes for functions, or While/For/If nodes
var queue = [], ramUsage = CONSTANTS.ScriptBaseRamCost;
var whileUsed = false, forUsed = false, ifUsed = false;
queue.push(ast);
while (queue.length != 0) {
var exp = queue.shift();
switch (exp.type) {
case "ImportDeclaration":
//Gets an array of all imported functions as AST expressions
//and pushes them on the queue.
var res = evaluateImport(exp, workerScript, true);
for (var i = 0; i < res.length; ++i) {
queue.push(res[i]);
}
break;
case "BlockStatement":
case "Program":
for (var i = 0; i < exp.body.length; ++i) {
if (exp.body[i] instanceof Node) {
queue.push(exp.body[i]);
}
}
break;
case "WhileStatement":
if (!whileUsed) {
ramUsage += CONSTANTS.ScriptWhileRamCost;
whileUsed = true;
}
break;
case "ForStatement":
if (!forUsed) {
ramUsage += CONSTANTS.ScriptForRamCost;
forUsed = true;
}
break;
case "IfStatement":
if (!ifUsed) {
ramUsage += CONSTANTS.ScriptIfRamCost;
ifUsed = true;
}
break;
case "Identifier":
if (exp.name in workerScript.env.vars) {
var func = workerScript.env.get(exp.name);
if (typeof func === "function") {
try {
var res = func.apply(null, []);
if (typeof res === "number") {
ramUsage += res;
}
} catch(e) {
console.log("ERROR applying function: " + e);
}
}
}
break;
default:
break;
}
for (var prop in exp) {
if (exp.hasOwnProperty(prop)) {
if (exp[prop] instanceof Node) {
queue.push(exp[prop]);
}
}
}
}
//Special case: hacknetnodes array
if (codeCopy.includes("hacknet")) {
ramUsage += CONSTANTS.ScriptHacknetNodesRamCost;
}
return ramUsage;
return -1;
}
+11 -39
View File
@@ -1,16 +1,17 @@
// 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 { AllServers } from "../Server/AllServers";
import { Settings } from "../Settings/Settings";
import { IMap } from "../types";
import { post } from "../ui/postToTerminal";
import { Script } from "./Script";
import { FconfSettings } from "../Fconf/FconfSettings";
import { Settings } from "../Settings/Settings";
import { IMap } from "../types";
import { post } from "../ui/postToTerminal";
import { Generic_fromJSON,
Generic_toJSON,
Reviver } from "../../utils/JSONReviver";
import { getTimestamp } from "../../utils/helpers/getTimestamp";
import {
Generic_fromJSON,
Generic_toJSON,
Reviver
} from "../../utils/JSONReviver";
import { getTimestamp } from "../../utils/helpers/getTimestamp";
export class RunningScript {
// Initializes a RunningScript Object from a JSON save state
@@ -73,35 +74,6 @@ export class RunningScript {
this.ramUsage = script.ramUsage;
}
getCode(): string {
const server = AllServers[this.server];
if (server == null) { return ""; }
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === this.filename) {
return server.scripts[i].code;
}
}
return "";
}
getRamUsage(): number {
if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value
const server = AllServers[this.server];
if (server == null) { return 0; }
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === this.filename) {
// Cache the ram usage for the next call
this.ramUsage = server.scripts[i].ramUsage;
return this.ramUsage;
}
}
return 0;
}
log(txt: string): void {
if (this.logs.length > Settings.MaxLogCapacity) {
//Delete first element and add new log entry to the end.
+20
View File
@@ -0,0 +1,20 @@
import { AllServers } from "../Server/AllServers";
import { RunningScript } from "./RunningScript";
export function getRamUsageFromRunningScript(script: RunningScript): number {
if (script.ramUsage != null && script.ramUsage > 0) {
return script.ramUsage; // Use cached value
}
const server = AllServers[script.server];
if (server == null) { return 0; }
for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === script.filename) {
// Cache the ram usage for the next call
script.ramUsage = server.scripts[i].ramUsage;
return script.ramUsage;
}
}
return 0;
}
+8 -8
View File
@@ -2,14 +2,14 @@
// This does NOT represent a script that is actively running and
// being evaluated. See RunningScript for that
import { calculateRamUsage } from "./RamCalculations";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Page,
routing } from "../ui/navigationTracking";
import { Page, routing } from "../ui/navigationTracking";
import { setTimeoutRef } from "../utils/SetTimeoutRef";
import { Generic_fromJSON,
Generic_toJSON,
Reviver } from "../../utils/JSONReviver";
import {
Generic_fromJSON,
Generic_toJSON,
Reviver
} from "../../utils/JSONReviver";
import { roundToTwo } from "../../utils/helpers/roundToTwo";
export class Script {
@@ -64,7 +64,7 @@ export class Script {
}
// Save a script FROM THE SCRIPT EDITOR
saveScript(code: string, p: IPlayer): void {
saveScript(code: string, currServ: string): void {
if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename
this.code = code.replace(/^\s+|\s+$/g, '');
@@ -77,7 +77,7 @@ export class Script {
this.filename = filenameElem!.value;
// Server
this.server = p.currentServer;
this.server = currServ;
//Calculate/update ram usage, execution time, etc.
this.updateRamUsage();
+40 -68
View File
@@ -1,33 +1,38 @@
import { Script } from "./Script";
import { Script } from "./Script";
import { calculateRamUsage } from "./RamCalculations";
import { isScriptFilename } from "./ScriptHelpersTS";
import { calculateRamUsage } from "./RamCalculations";
import { isScriptFilename } from "./ScriptHelpersTS";
import {CONSTANTS} from "../Constants";
import {Engine} from "../engine";
import { parseFconfSettings } from "../Fconf/Fconf";
import { FconfSettings } from "../Fconf/FconfSettings";
import {iTutorialSteps, iTutorialNextStep,
ITutorial} from "../InteractiveTutorial";
import { addWorkerScript } from "../NetscriptWorker";
import { Player } from "../Player";
import { AceEditor } from "../ScriptEditor/Ace";
import { CodeMirrorEditor } from "../ScriptEditor/CodeMirror";
import { AllServers } from "../Server/AllServers";
import { processSingleServerGrowth } from "../Server/ServerHelpers";
import { Settings } from "../Settings/Settings";
import { EditorSetting } from "../Settings/SettingEnums";
import { isValidFilePath } from "../Terminal/DirectoryHelpers";
import {TextFile} from "../TextFile";
import {CONSTANTS} from "../Constants";
import {Engine} from "../engine";
import { parseFconfSettings } from "../Fconf/Fconf";
import { FconfSettings } from "../Fconf/FconfSettings";
import {
iTutorialSteps,
iTutorialNextStep,
ITutorial
} from "../InteractiveTutorial";
import { Player } from "../Player";
import { AceEditor } from "../ScriptEditor/Ace";
import { CodeMirrorEditor } from "../ScriptEditor/CodeMirror";
import { AllServers } from "../Server/AllServers";
import { processSingleServerGrowth } from "../Server/ServerHelpers";
import { Settings } from "../Settings/Settings";
import { EditorSetting } from "../Settings/SettingEnums";
import { isValidFilePath } from "../Terminal/DirectoryHelpers";
import { TextFile } from "../TextFile";
import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "../ui/numeralFormat";
import { Page, routing } from "../ui/navigationTracking";
import { numeralWrapper } from "../ui/numeralFormat";
import {dialogBoxCreate} from "../../utils/DialogBox";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../../utils/JSONReviver";
import {compareArrays} from "../../utils/helpers/compareArrays";
import {createElement} from "../../utils/uiHelpers/createElement";
import { dialogBoxCreate } from "../../utils/DialogBox";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../utils/JSONReviver";
import { compareArrays } from "../../utils/helpers/compareArrays";
import { createElement } from "../../utils/uiHelpers/createElement";
var scriptEditorRamCheck = null, scriptEditorRamText = null;
export function scriptEditorInit() {
@@ -127,20 +132,22 @@ export function scriptEditorInit() {
editorSelector.onchange = () => {
const opt = editorSelector.value;
switch (opt) {
case EditorSetting.Ace:
case EditorSetting.Ace: {
const codeMirrorCode = CodeMirrorEditor.getCode();
const codeMirrorFn = CodeMirrorEditor.getFilename();
AceEditor.create();
CodeMirrorEditor.setInvisible();
AceEditor.openScript(codeMirrorFn, codeMirrorCode);
break;
case EditorSetting.CodeMirror:
}
case EditorSetting.CodeMirror: {
const aceCode = AceEditor.getCode();
const aceFn = AceEditor.getFilename();
CodeMirrorEditor.create();
AceEditor.setInvisible();
CodeMirrorEditor.openScript(aceFn, aceCode);
break;
}
default:
console.error(`Unrecognized Editor Setting: ${opt}`);
return;
@@ -229,7 +236,7 @@ function saveAndCloseScriptEditor() {
let s = Player.getCurrentServer();
for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player);
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer);
Engine.loadTerminalContent();
return iTutorialNextStep();
}
@@ -237,7 +244,7 @@ function saveAndCloseScriptEditor() {
//If the current script does NOT exist, create a new one
let script = new Script();
script.saveScript(getCurrentEditor().getCode(), Player);
script.saveScript(getCurrentEditor().getCode(), Player.currentServer);
s.scripts.push(script);
return iTutorialNextStep();
@@ -265,7 +272,7 @@ function saveAndCloseScriptEditor() {
//If the current script already exists on the server, overwrite it
for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player);
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer);
Engine.loadTerminalContent();
return;
}
@@ -273,7 +280,7 @@ function saveAndCloseScriptEditor() {
//If the current script does NOT exist, create a new one
const script = new Script();
script.saveScript(getCurrentEditor().getCode(), Player);
script.saveScript(getCurrentEditor().getCode(), Player.currentServer);
s.scripts.push(script);
} else if (filename.endsWith(".txt")) {
for (var i = 0; i < s.textFiles.length; ++i) {
@@ -293,42 +300,7 @@ function saveAndCloseScriptEditor() {
Engine.loadTerminalContent();
}
//Called when the game is loaded. Loads all running scripts (from all servers)
//into worker scripts so that they will start running
export function loadAllRunningScripts() {
var total = 0;
let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1);
if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); }
for (var property in AllServers) {
if (AllServers.hasOwnProperty(property)) {
var server = AllServers[property];
//Reset each server's RAM usage to 0
server.ramUsed = 0;
//Reset modules on all scripts
for (var i = 0; i < server.scripts.length; ++i) {
server.scripts[i].module = "";
}
if (skipScriptLoad) {
//Start game with no scripts
server.runningScripts.length = 0;
} else {
for (var j = 0; j < server.runningScripts.length; ++j) {
addWorkerScript(server.runningScripts[j], server);
//Offline production
total += scriptCalculateOfflineProduction(server.runningScripts[j]);
}
}
}
}
return total;
}
function scriptCalculateOfflineProduction(runningScriptObj) {
export function scriptCalculateOfflineProduction(runningScriptObj) {
//The Player object stores the last update time from when we were online
var thisUpdate = new Date().getTime();
var lastUpdate = Player.lastUpdate;