Refactored Server/Script/Files code to TypeScript

This commit is contained in:
danielyxie 2019-03-04 17:40:28 -08:00
parent e1b8a23f1e
commit 473f0f1447
34 changed files with 763 additions and 695 deletions

@ -1,7 +1,7 @@
import {workerScripts, import {workerScripts,
killWorkerScript} from "./NetscriptWorker"; killWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import { Player } from "./Player";
import {getServer} from "./Server"; import { getServer } from "./Server/ServerHelpers";
import {numeralWrapper} from "./ui/numeralFormat"; import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../utils/DialogBox";
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement"; import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";

@ -12,9 +12,9 @@ import { addWorkerScript } from "../NetscriptWorker";
import { Player } from "../Player"; import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { saveObject } from "../SaveObject"; import { saveObject } from "../SaveObject";
import { Script, import { RunningScript } from "../Script/RunningScript";
RunningScript} from "../Script"; import { Script } from "../Script/Script";
import { Server } from "../Server"; import { Server } from "../Server/Server";
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";

@ -3,8 +3,8 @@ import { CodingContract,
CodingContractTypes } from "./CodingContracts"; CodingContractTypes } from "./CodingContracts";
import { Factions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { Player } from "./Player"; import { Player } from "./Player";
import { GetServerByHostname, import { AllServers } from "./Server/Server";
AllServers } from "./Server"; import { GetServerByHostname } from "./Server/ServerHelpers";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";

@ -1,7 +1,7 @@
import { DarkWebItems } from "./DarkWebItems"; import { DarkWebItems } from "./DarkWebItems";
import { Player } from "../Player"; import { Player } from "../Player";
import { SpecialServerIps } from "../SpecialServerIps"; import { SpecialServerIps } from "../Server/SpecialServerIps";
import { post } from "../ui/postToTerminal"; import { post } from "../ui/postToTerminal";
import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress"; import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress";

@ -8,7 +8,7 @@ import { Company } from "./Company/Company";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { Player } from "./Player"; import { Player } from "./Player";
import { AllServers } from "./Server"; import { AllServers } from "./Server/AllServers";
import { hackWorldDaemon } from "./RedPill"; import { hackWorldDaemon } from "./RedPill";
import { StockMarket, import { StockMarket,
SymbolToStockMap } from "./StockMarket/StockMarket"; SymbolToStockMap } from "./StockMarket/StockMarket";

@ -1,16 +1,7 @@
import {parse, Node} from "../utils/acorn"; import { FconfSettings } from "./FconfSettings";
import {dialogBoxCreate} from "../utils/DialogBox";
var FconfSettings = { import { parse, Node } from "../../utils/acorn";
ENABLE_BASH_HOTKEYS: false, import { dialogBoxCreate } from "../../utils/DialogBox";
ENABLE_TIMESTAMPS: false,
MAIN_MENU_STYLE: "default",
THEME_BACKGROUND_COLOR: "#000000",
THEME_FONT_COLOR: "#66ff33",
THEME_HIGHLIGHT_COLOR: "#ffffff",
THEME_PROMPT_COLOR: "#f92672",
WRAP_INPUT: false,
}
var FconfComments = { var FconfComments = {
ENABLE_BASH_HOTKEYS: "Improved Bash emulation mode. Setting this to 1 enables several\n" + ENABLE_BASH_HOTKEYS: "Improved Bash emulation mode. Setting this to 1 enables several\n" +

@ -0,0 +1,10 @@
export const FconfSettings = {
ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false,
MAIN_MENU_STYLE: "default",
THEME_BACKGROUND_COLOR: "#000000",
THEME_FONT_COLOR: "#66ff33",
THEME_HIGHLIGHT_COLOR: "#ffffff",
THEME_PROMPT_COLOR: "#f92672",
WRAP_INPUT: false,
}

@ -1,6 +1,6 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { Player } from "./Player"; import { Player } from "./Player";
import { Server } from "./Server"; import { Server } from "./Server/Server";
/** /**
* Returns the chance the player has to successfully hack a server * Returns the chance the player has to successfully hack a server

@ -11,13 +11,16 @@ import {beginInfiltration} from "./Infiltration";
import {hasBladeburnerSF} from "./NetscriptFunctions"; import {hasBladeburnerSF} from "./NetscriptFunctions";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
import {Player} from "./Player"; import {Player} from "./Player";
import {Server, AllServers, AddToAllServers} from "./Server"; import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { AddToAllServers } from "./Server/ServerHelpers";
import { getPurchaseServerCost, import { getPurchaseServerCost,
purchaseServer, purchaseServer,
purchaseRamForHomeComputer} from "./ServerPurchases"; purchaseRamForHomeComputer } from "./Server/ServerPurchases";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps"; import { SpecialServerNames,
SpecialServerIps } from "./Server/SpecialServerIps";
import {numeralWrapper} from "./ui/numeralFormat"; import {numeralWrapper} from "./ui/numeralFormat";

@ -12,7 +12,7 @@ export class Message {
filename: string = ""; filename: string = "";
// The text contains in the Message // The text contains in the Message
msg: string = "": msg: string = "";
// Flag indicating whether this Message has been received by the player // Flag indicating whether this Message has been received by the player
recvd: boolean = false; recvd: boolean = false;

@ -6,7 +6,7 @@ import { Programs } from "../Programs/Programs";
import { inMission } from "../Missions"; import { inMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { redPillFlag } from "../RedPill"; import { redPillFlag } from "../RedPill";
import { GetServerByHostname } from "../Server"; import { GetServerByHostname } from "../Server/ServerHelpers";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { dialogBoxCreate, import { dialogBoxCreate,
dialogBoxOpened} from "../../utils/DialogBox"; dialogBoxOpened} from "../../utils/DialogBox";

@ -3,10 +3,12 @@ import { CONSTANTS } from "./Constants";
import { Player } from "./Player"; import { Player } from "./Player";
import { Environment } from "./NetscriptEnvironment"; import { Environment } from "./NetscriptEnvironment";
import { WorkerScript, addWorkerScript} from "./NetscriptWorker"; import { WorkerScript, addWorkerScript} from "./NetscriptWorker";
import { Server, getServer} from "./Server"; import { Server } from "./Server/Server";
import { getServer } from "./Server/ServerHelpers";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { Script, findRunningScript, import { RunningScript } from "./Script/RunningScript";
RunningScript } from "./Script"; import { Script } from "./Script/Script";
import { findRunningScript } from "./Script/ScriptHelpers";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";

@ -31,20 +31,26 @@ import { joinFaction,
import { getCostOfNextHacknetNode, import { getCostOfNextHacknetNode,
purchaseHacknet } from "./HacknetNode"; purchaseHacknet } from "./HacknetNode";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
import {Message, Messages} from "./Message"; import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers";
import {inMission} from "./Missions"; import {inMission} from "./Missions";
import {Player} from "./Player"; import {Player} from "./Player";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
import {Script, findRunningScript, RunningScript, import { Script } from "./Script/Script";
isScriptFilename} from "./Script"; import { findRunningScript } from "./Script/ScriptHelpers";
import {Server, getServer, AddToAllServers, import { isScriptFilename } from "./Script/ScriptHelpersTS";
AllServers, processSingleServerGrowth, import { AllServers,
GetServerByHostname, numCycleForGrowth} from "./Server"; AddToAllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { GetServerByHostname,
getServer,
numCycleForGrowth,
processSingleServerGrowth } from "./Server/ServerHelpers";
import { getPurchaseServerCost, import { getPurchaseServerCost,
getPurchaseServerLimit, getPurchaseServerLimit,
getPurchaseServerMaxRam } from "./ServerPurchases"; getPurchaseServerMaxRam } from "./Server/ServerPurchases";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps} from "./SpecialServerIps"; import {SpecialServerIps} from "./Server/SpecialServerIps";
import {Stock} from "./StockMarket/Stock"; import {Stock} from "./StockMarket/Stock";
import {StockMarket, StockSymbols, SymbolToStockMap, import {StockMarket, StockSymbols, SymbolToStockMap,
initStockMarket, initSymbolToStockMap, buyStock, initStockMarket, initSymbolToStockMap, buyStock,
@ -305,9 +311,9 @@ function NetscriptFunctions(workerScript) {
for (var i = 0; i < server.serversOnNetwork.length; i++) { for (var i = 0; i < server.serversOnNetwork.length; i++) {
var entry; var entry;
if (hostnames) { if (hostnames) {
entry = server.getServerOnNetwork(i).hostname; entry = getServerOnNetwork(server, i).hostname;
} else { } else {
entry = server.getServerOnNetwork(i).ip; entry = getServerOnNetwork(server, i).ip;
} }
if (entry == null) { if (entry == null) {
continue; continue;
@ -483,7 +489,7 @@ function NetscriptFunctions(workerScript) {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable; const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
server.moneyAvailable += (1 * threads); //It can be grown even if it has no money server.moneyAvailable += (1 * threads); //It can be grown even if it has no money
var growthPercentage = processSingleServerGrowth(server, 450 * threads); var growthPercentage = processSingleServerGrowth(server, 450 * threads, Player);
const moneyAfter = server.moneyAvailable; const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads); workerScript.scriptRef.recordGrow(server.ip, threads);
var expGain = calculateHackingExpGain(server) * threads; var expGain = calculateHackingExpGain(server) * threads;
@ -512,7 +518,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${growth}. Must be numeric`); throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${growth}. Must be numeric`);
} }
return numCycleForGrowth(server, Number(growth)); return numCycleForGrowth(server, Number(growth), Player);
}, },
weaken : function(ip) { weaken : function(ip) {
if (workerScript.checkingRam) { if (workerScript.checkingRam) {

@ -11,7 +11,7 @@ import {evaluate, isScriptErrorMessage,
import {NetscriptFunctions} from "./NetscriptFunctions"; import {NetscriptFunctions} from "./NetscriptFunctions";
import {executeJSScript} from "./NetscriptJSEvaluator"; import {executeJSScript} from "./NetscriptJSEvaluator";
import {NetscriptPort} from "./NetscriptPort"; import {NetscriptPort} from "./NetscriptPort";
import {AllServers} from "./Server"; import { AllServers } from "./Server/AllServers";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";

@ -20,6 +20,7 @@ export interface IPlayer {
city: string; city: string;
companyName: string; companyName: string;
corporation: any; corporation: any;
currentServer: string;
factions: string[]; factions: string[];
hacknetNodes: any[]; hacknetNodes: any[];
hasWseAccount: boolean; hasWseAccount: boolean;

@ -24,9 +24,11 @@ import {Gang, resetGangs} from "./Gang";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions"; import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import {AllServers, Server, AddToAllServers} from "./Server"; import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { AddToAllServers } from "./Server/ServerHelpers";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps"; import {SpecialServerIps, SpecialServerNames} from "./Server/SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile"; import {SourceFiles, applySourceFile} from "./SourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import Decimal from "decimal.js"; import Decimal from "decimal.js";

@ -16,20 +16,25 @@ import { Factions,
import { joinFaction } from "./Faction/FactionHelpers"; import { joinFaction } from "./Faction/FactionHelpers";
import {deleteGangDisplayContent} from "./Gang"; import {deleteGangDisplayContent} from "./Gang";
import {Locations} from "./Location"; import {Locations} from "./Location";
import {initMessages, Messages, Message} from "./Message"; import { initMessages,
Messages,
Message } from "./Message/MessageHelpers";
import {initSingularitySFFlags, hasWallStreetSF}from "./NetscriptFunctions"; import {initSingularitySFFlags, hasWallStreetSF}from "./NetscriptFunctions";
import {WorkerScript, workerScripts, import {WorkerScript, workerScripts,
prestigeWorkerScripts} from "./NetscriptWorker"; prestigeWorkerScripts} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import {AllServers, AddToAllServers, import { AllServers } from "./Server/AllServers";
initForeignServers, Server, import { Server } from "./Server/Server"
prestigeAllServers, import { AddToAllServers,
prestigeHomeComputer} from "./Server"; initForeignServers,
prestigeAllServers,
prestigeHomeComputer } from "./Server/ServerHelpers";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerIps, SpecialServerIpsMap, import { SpecialServerIps,
prestigeSpecialServerIps, SpecialServerIpsMap,
SpecialServerNames} from "./SpecialServerIps"; prestigeSpecialServerIps,
SpecialServerNames} from "./Server/SpecialServerIps";
import {initStockMarket, initSymbolToStockMap, import {initStockMarket, initSymbolToStockMap,
stockMarketContentCreated, stockMarketContentCreated,
setStockMarketContentCreated} from "./StockMarket/StockMarket"; setStockMarketContentCreated} from "./StockMarket/StockMarket";
@ -89,7 +94,7 @@ function prestigeAugmentation() {
} }
//Re-create foreign servers //Re-create foreign servers
initForeignServers(); initForeignServers(Player.getHomeComputer());
//Darkweb is purchase-able //Darkweb is purchase-able
document.getElementById("location-purchase-tor").setAttribute("class", "a-link-button"); document.getElementById("location-purchase-tor").setAttribute("class", "a-link-button");
@ -194,7 +199,7 @@ function prestigeSourceFile() {
prestigeHomeComputer(homeComp); prestigeHomeComputer(homeComp);
//Re-create foreign servers //Re-create foreign servers
initForeignServers(); initForeignServers(Player.getHomeComputer());
var srcFile1Owned = false; var srcFile1Owned = false;
for (var i = 0; i < Player.sourceFiles.length; ++i) { for (var i = 0; i < Player.sourceFiles.length; ++i) {

@ -7,15 +7,18 @@ import {Engine} from "./engine";
import { Factions, import { Factions,
loadFactions } from "./Faction/Factions"; loadFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain } from "./Faction/FactionHelpers"; import { processPassiveFactionRepGain } from "./Faction/FactionHelpers";
import {FconfSettings, loadFconf} from "./Fconf"; import { loadFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import {loadAllGangs, AllGangs} from "./Gang"; import {loadAllGangs, AllGangs} from "./Gang";
import {processAllHacknetNodeEarnings} from "./HacknetNode"; import {processAllHacknetNodeEarnings} from "./HacknetNode";
import {loadMessages, initMessages, Messages} from "./Message"; import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers";
import {Player, loadPlayer} from "./Player"; import {Player, loadPlayer} from "./Player";
import {loadAllRunningScripts} from "./Script"; import { loadAllRunningScripts } from "./Script/ScriptHelpers";
import {AllServers, loadAllServers} from "./Server"; import { AllServers } from "./Server/AllServers";
import {Settings} from "./Settings/Settings"; import { loadAllServers } from "./Server/ServerHelpers";
import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps"; import { Settings } from "./Settings/Settings";
import { loadSpecialServerIps,
SpecialServerIps } from "./Server/SpecialServerIps";
import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket"; import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";

1
src/Script/RamCalculations.d.ts vendored Normal file

@ -0,0 +1 @@
export declare function calculateRamUsage(codeCopy: string): number;

@ -0,0 +1,409 @@
// 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";
// These special strings are used to reference the presence of a given logical
// construct within a user script.
const specialReferenceIF = "__SPECIAL_referenceIf";
const specialReferenceFOR = "__SPECIAL_referenceFor";
const specialReferenceWHILE = "__SPECIAL_referenceWhile";
// The global scope of a script is registered under this key during parsing.
const memCheckGlobalKey = ".__GLOBAL__";
// Calcluates the amount of RAM a script uses. Uses parsing and AST walking only,
// rather than NetscriptEvaluator. This is useful because NetscriptJS code does
// not work under NetscriptEvaluator.
async function parseOnlyRamCalculate(server, code, workerScript) {
try {
// Maps dependent identifiers to their dependencies.
//
// The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__.
// It depends on all the functions declared in the module, all the global scopes
// of its imports, and any identifiers referenced in this global scope. Each
// function depends on all the identifiers referenced internally.
// We walk the dependency graph to calculate RAM usage, given that some identifiers
// reference Netscript functions which have a RAM cost.
let dependencyMap = {};
// Scripts we've parsed.
const completedParses = new Set();
// Scripts we've discovered that need to be parsed.
const parseQueue = [];
// Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap.
function parseCode(code, moduleName) {
const result = parseOnlyCalculateDeps(code, moduleName);
completedParses.add(moduleName);
// Add any additional modules to the parse queue;
for (let i = 0; i < result.additionalModules.length; ++i) {
if (!completedParses.has(result.additionalModules[i])) {
parseQueue.push(result.additionalModules[i]);
}
}
// Splice all the references in.
//Spread syntax not supported in edge, use Object.assign instead
//dependencyMap = {...dependencyMap, ...result.dependencyMap};
dependencyMap = Object.assign(dependencyMap, result.dependencyMap);
}
const initialModule = "__SPECIAL_INITIAL_MODULE__";
parseCode(code, initialModule);
while (parseQueue.length > 0) {
// Get the code from the server.
const nextModule = parseQueue.shift();
let code;
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) {
try {
const module = await eval('import(nextModule)');
code = "";
for (const prop in module) {
if (typeof module[prop] === 'function') {
code += module[prop].toString() + ";\n";
}
}
} catch(e) {
console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`);
return -1;
}
} else {
const script = server.getScript(nextModule.startsWith("./") ? nextModule.slice(2) : nextModule);
if (!script) {
console.warn("Invalid script");
return -1; // No such script on the server.
}
code = script.code;
}
parseCode(code, nextModule);
}
// 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;
const unresolvedRefs = Object.keys(dependencyMap).filter(s => s.startsWith(initialModule));
const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) {
const ref = unresolvedRefs.shift();
// 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;
}
if (ref === "document" && !resolvedRefs.has("document")) {
ram += CONSTANTS.ScriptDomRamCost;
}
if (ref === "window" && !resolvedRefs.has("window")) {
ram += CONSTANTS.ScriptDomRamCost;
}
resolvedRefs.add(ref);
if (ref.endsWith(".*")) {
// A prefix reference. We need to find all matching identifiers.
const prefix = ref.slice(0, ref.length - 2);
for (let ident of Object.keys(dependencyMap).filter(k => k.startsWith(prefix))) {
for (let dep of dependencyMap[ident] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
} else {
// An exact reference. Add all dependencies of this ref.
for (let dep of dependencyMap[ref] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
// 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.
//
// TODO it would be simpler to just reference a dictionary.
try {
function applyFuncRam(func) {
if (typeof func === "function") {
try {
let res;
if (func.constructor.name === "AsyncFunction") {
res = 0; // Async functions will always be 0 RAM
} else {
res = func.apply(null, []);
}
if (typeof res === "number") {
return res;
}
return 0;
} catch(e) {
console.log("ERROR applying function: " + e);
return 0;
}
} else {
return 0;
}
}
//Special logic for namespaces (Bladeburner, CodingCOntract)
var func;
if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref];
} else if (ref in workerScript.env.vars.codingcontract) {
func = workerScript.env.vars.codingcontract[ref];
} else if (ref in workerScript.env.vars.gang) {
func = workerScript.env.vars.gang[ref];
} else {
func = workerScript.env.get(ref);
}
ram += applyFuncRam(func);
} catch (error) {continue;}
}
return ram;
} catch (error) {
// console.info("parse or eval error: ", error);
// This is not unexpected. The user may be editing a script, and it may be in
// a transitory invalid state.
return -1;
}
}
// Parses one script and calculates its ram usage, for the global scope and each function.
// Returns a cost map and a dependencyMap for the module. Returns a reference map to be joined
// onto the main reference map, and a list of modules that need to be parsed.
function parseOnlyCalculateDeps(code, currentModule) {
const ast = parse(code, {sourceType:"module", ecmaVersion: 8});
// Everything from the global scope goes in ".". Everything else goes in ".function", where only
// the outermost layer of functions counts.
const globalKey = currentModule + memCheckGlobalKey;
const dependencyMap = {};
dependencyMap[globalKey] = new Set();
// If we reference this internal name, we're really referencing that external name.
// Filled when we import names from other modules.
let internalToExternal = {};
var additionalModules = [];
// References get added pessimistically. They are added for thisModule.name, name, and for
// any aliases.
function addRef(key, name) {
const s = dependencyMap[key] || (dependencyMap[key] = new Set());
if (name in internalToExternal) {
s.add(internalToExternal[name]);
}
s.add(currentModule + "." + name);
s.add(name); // For builtins like hack.
}
//A list of identifiers that resolve to "native Javascript code"
const objectPrototypeProperties = Object.getOwnPropertyNames(Object.prototype);
// If we discover a dependency identifier, state.key is the dependent identifier.
// walkDeeper is for doing recursive walks of expressions in composites that we handle.
function commonVisitors() {
return {
Identifier: (node, st, walkDeeper) => {
if (objectPrototypeProperties.includes(node.name)) {return;}
addRef(st.key, node.name);
},
WhileStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st);
},
DoWhileStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st);
},
ForStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceFOR);
node.init && walkDeeper(node.init, st);
node.test && walkDeeper(node.test, st);
node.update && walkDeeper(node.update, st);
node.body && walkDeeper(node.body, st);
},
IfStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceIF);
node.test && walkDeeper(node.test, st);
node.consequent && walkDeeper(node.consequent, st);
node.alternate && walkDeeper(node.alternate, st);
},
MemberExpression: (node, st, walkDeeper) => {
node.object && walkDeeper(node.object, st);
node.property && walkDeeper(node.property, st);
},
}
}
//Spread syntax not supported in Edge yet, use Object.assign
/*
walk.recursive(ast, {key: globalKey}, {
ImportDeclaration: (node, st, walkDeeper) => {
const importModuleName = node.source.value;
additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we
// import it.
dependencyMap[st.key].add(importModuleName + memCheckGlobalKey);
for (let i = 0; i < node.specifiers.length; ++i) {
const spec = node.specifiers[i];
if (spec.imported !== undefined && spec.local !== undefined) {
// We depend on specific things.
internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name;
} else {
// We depend on everything.
dependencyMap[st.key].add(importModuleName + ".*");
}
}
},
FunctionDeclaration: (node, st, walkDeeper) => {
// Don't use walkDeeper, because we are changing the visitor set.
const key = currentModule + "." + node.id.name;
walk.recursive(node, {key: key}, commonVisitors());
},
...commonVisitors()
});
*/
walk.recursive(ast, {key: globalKey}, Object.assign({
ImportDeclaration: (node, st, walkDeeper) => {
const importModuleName = node.source.value;
additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we
// import it.
dependencyMap[st.key].add(importModuleName + memCheckGlobalKey);
for (let i = 0; i < node.specifiers.length; ++i) {
const spec = node.specifiers[i];
if (spec.imported !== undefined && spec.local !== undefined) {
// We depend on specific things.
internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name;
} else {
// We depend on everything.
dependencyMap[st.key].add(importModuleName + ".*");
}
}
},
FunctionDeclaration: (node, st, walkDeeper) => {
// Don't use walkDeeper, because we are changing the visitor set.
const key = currentModule + "." + node.id.name;
walk.recursive(node, {key: key}, commonVisitors());
},
}, commonVisitors()));
return {dependencyMap: dependencyMap, additionalModules: additionalModules};
}
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;
try {
return await parseOnlyRamCalculate(currServ, codeCopy, workerScript);
} catch (e) {
console.log("Failed to parse ram using new method. Falling back.", e);
}
// 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;
}

@ -1,10 +1,16 @@
// Class representing a Script instance that is actively running. // Class representing a Script instance that is actively running.
// A Script can have multiple active instances // A Script can have multiple active instances
import { Script } from "./Script"; import { Script } from "./Script";
import { IMap } from "../types"; 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 { Generic_fromJSON, import { Generic_fromJSON,
Generic_toJSON, Generic_toJSON,
Reviver } from "../../utils/JSONReviver"; Reviver } from "../../utils/JSONReviver";
import { getTimestamp } from "../../utils/helpers/getTimestamp";
export class RunningScript { export class RunningScript {
// Initializes a RunningScript Object from a JSON save state // Initializes a RunningScript Object from a JSON save state
@ -67,7 +73,7 @@ export class RunningScript {
this.ramUsage = script.ramUsage; this.ramUsage = script.ramUsage;
} }
RunningScript.prototype.getCode = function() { getCode(): string {
const server = AllServers[this.server]; const server = AllServers[this.server];
if (server == null) { return ""; } if (server == null) { return ""; }
for (let i = 0; i < server.scripts.length; ++i) { for (let i = 0; i < server.scripts.length; ++i) {
@ -79,7 +85,7 @@ export class RunningScript {
return ""; return "";
} }
RunningScript.prototype.getRamUsage = function() { getRamUsage(): number {
if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value
const server = AllServers[this.server]; const server = AllServers[this.server];
@ -96,7 +102,7 @@ export class RunningScript {
return 0; return 0;
} }
RunningScript.prototype.log = function(txt) { log(txt: string): void {
if (this.logs.length > Settings.MaxLogCapacity) { if (this.logs.length > Settings.MaxLogCapacity) {
//Delete first element and add new log entry to the end. //Delete first element and add new log entry to the end.
//TODO Eventually it might be better to replace this with circular array //TODO Eventually it might be better to replace this with circular array
@ -111,18 +117,18 @@ export class RunningScript {
this.logUpd = true; this.logUpd = true;
} }
RunningScript.prototype.displayLog = function() { displayLog(): void {
for (var i = 0; i < this.logs.length; ++i) { for (var i = 0; i < this.logs.length; ++i) {
post(this.logs[i]); post(this.logs[i]);
} }
} }
RunningScript.prototype.clearLog = function() { clearLog(): void {
this.logs.length = 0; this.logs.length = 0;
} }
//Update the moneyStolen and numTimesHack maps when hacking // Update the moneyStolen and numTimesHack maps when hacking
RunningScript.prototype.recordHack = function(serverIp, moneyGained, n=1) { recordHack(serverIp: string, moneyGained: number, n: number=1) {
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
this.dataMap[serverIp] = [0, 0, 0, 0]; this.dataMap[serverIp] = [0, 0, 0, 0];
} }
@ -130,16 +136,16 @@ export class RunningScript {
this.dataMap[serverIp][1] += n; this.dataMap[serverIp][1] += n;
} }
//Update the grow map when calling grow() // Update the grow map when calling grow()
RunningScript.prototype.recordGrow = function(serverIp, n=1) { recordGrow(serverIp: string, n: number=1) {
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
this.dataMap[serverIp] = [0, 0, 0, 0]; this.dataMap[serverIp] = [0, 0, 0, 0];
} }
this.dataMap[serverIp][2] += n; this.dataMap[serverIp][2] += n;
} }
//Update the weaken map when calling weaken() { // Update the weaken map when calling weaken() {
RunningScript.prototype.recordWeaken = function(serverIp, n=1) { recordWeaken(serverIp: string, n: number=1) {
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
this.dataMap[serverIp] = [0, 0, 0, 0]; this.dataMap[serverIp] = [0, 0, 0, 0];
} }

@ -1,8 +1,11 @@
// Class representing a script file // Class representing a script file
// This does NOT represent a script that is actively running and // This does NOT represent a script that is actively running and
// being evaluated. See RunningScript for that // being evaluated. See RunningScript for that
import { calculateRamUsage } from "./RamCalculations";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Page, import { Page,
routing } from "../ui/navigationTracking"; routing } from "../ui/navigationTracking";
import { setTimeoutRef } from "../utils/SetTimeoutRef"; import { setTimeoutRef } from "../utils/SetTimeoutRef";
import { Generic_fromJSON, import { Generic_fromJSON,
Generic_toJSON, Generic_toJSON,
@ -61,10 +64,9 @@ export class Script {
} }
// Save a script FROM THE SCRIPT EDITOR // Save a script FROM THE SCRIPT EDITOR
saveScript(): void { saveScript(code: string, p: IPlayer): void {
if (routing.isOn(Page.ScriptEditor)) { if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename //Update code and filename
const code = getCurrentEditor().getCode();
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,7 +77,7 @@ export class Script {
this.filename = filenameElem!.value; this.filename = filenameElem!.value;
// Server // Server
this.server = Player.currentServer; this.server = p.currentServer;
//Calculate/update ram usage, execution time, etc. //Calculate/update ram usage, execution time, etc.
this.updateRamUsage(); this.updateRamUsage();
@ -85,7 +87,7 @@ export class Script {
} }
// Updates the script's RAM usage based on its code // Updates the script's RAM usage based on its code
async updateRamUsage(): void { async updateRamUsage() {
// TODO Commented this out because I think its unnecessary // TODO Commented this out because I think its unnecessary
// DOuble check/Test // DOuble check/Test
// var codeCopy = this.code.repeat(1); // var codeCopy = this.code.repeat(1);

@ -1,40 +1,33 @@
// Importing this doesn't work for some reason. import { calculateRamUsage } from "./RamCalculations";
const walk = require("acorn/dist/walk"); import { isScriptFilename } from "./ScriptHelpersTS";
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "../Constants";
import {Engine} from "./engine"; import {Engine} from "../engine";
import {FconfSettings, parseFconfSettings} from "./Fconf"; import { parseFconfSettings } from "../Fconf/Fconf";
import { FconfSettings } from "../Fconf/FconfSettings";
import {iTutorialSteps, iTutorialNextStep, import {iTutorialSteps, iTutorialNextStep,
ITutorial} from "./InteractiveTutorial"; ITutorial} from "../InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator"; import { addWorkerScript } from "../NetscriptWorker";
import {NetscriptFunctions} from "./NetscriptFunctions"; import { Player } from "../Player";
import {addWorkerScript, WorkerScript} from "./NetscriptWorker"; import { AceEditor } from "../ScriptEditor/Ace";
import {Player} from "./Player"; import { CodeMirrorEditor } from "../ScriptEditor/CodeMirror";
import { AceEditor } from "./ScriptEditor/Ace"; import { AllServers } from "../Server/AllServers";
import { CodeMirrorEditor } from "./ScriptEditor/CodeMirror"; import { processSingleServerGrowth } from "../Server/ServerHelpers";
import {AllServers, processSingleServerGrowth} from "./Server"; import { Settings } from "../Settings/Settings";
import { Settings } from "./Settings/Settings"; import { EditorSetting } from "../Settings/SettingEnums";
import { EditorSetting } from "./Settings/SettingEnums"; import {TextFile} from "../TextFile";
import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
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 {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo";
function isScriptFilename(f) { import {Page, routing} from "../ui/navigationTracking";
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns"); 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";
var scriptEditorRamCheck = null, scriptEditorRamText = null; var scriptEditorRamCheck = null, scriptEditorRamText = null;
function scriptEditorInit() { export function scriptEditorInit() {
// Wrapper container that holds all the buttons below the script editor // Wrapper container that holds all the buttons below the script editor
const wrapper = document.getElementById("script-editor-buttons-wrapper"); const wrapper = document.getElementById("script-editor-buttons-wrapper");
if (wrapper == null) { if (wrapper == null) {
@ -233,7 +226,7 @@ function saveAndCloseScriptEditor() {
let s = Player.getCurrentServer(); let s = Player.getCurrentServer();
for (var i = 0; i < s.scripts.length; i++) { for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) { if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(); s.scripts[i].saveScript(getCurrentEditor().getCode(), Player);
Engine.loadTerminalContent(); Engine.loadTerminalContent();
return iTutorialNextStep(); return iTutorialNextStep();
} }
@ -241,7 +234,7 @@ function saveAndCloseScriptEditor() {
//If the current script does NOT exist, create a new one //If the current script does NOT exist, create a new one
let script = new Script(); let script = new Script();
script.saveScript(); script.saveScript(getCurrentEditor().getCode(), Player);
s.scripts.push(script); s.scripts.push(script);
return iTutorialNextStep(); return iTutorialNextStep();
@ -269,7 +262,7 @@ function saveAndCloseScriptEditor() {
//If the current script already exists on the server, overwrite it //If the current script already exists on the server, overwrite it
for (var i = 0; i < s.scripts.length; i++) { for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) { if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(); s.scripts[i].saveScript(getCurrentEditor().getCode(), Player);
Engine.loadTerminalContent(); Engine.loadTerminalContent();
return; return;
} }
@ -277,7 +270,7 @@ function saveAndCloseScriptEditor() {
//If the current script does NOT exist, create a new one //If the current script does NOT exist, create a new one
var script = new Script(); var script = new Script();
script.saveScript(); script.saveScript(getCurrentEditor().getCode(), Player);
s.scripts.push(script); s.scripts.push(script);
} else if (filename.endsWith(".txt")) { } else if (filename.endsWith(".txt")) {
for (var i = 0; i < s.textFiles.length; ++i) { for (var i = 0; i < s.textFiles.length; ++i) {
@ -308,410 +301,9 @@ function checkValidFilename(filename) {
return false; return false;
} }
// These special strings are used to reference the presence of a given logical
// construct within a user script.
const specialReferenceIF = "__SPECIAL_referenceIf";
const specialReferenceFOR = "__SPECIAL_referenceFor";
const specialReferenceWHILE = "__SPECIAL_referenceWhile";
// The global scope of a script is registered under this key during parsing.
const memCheckGlobalKey = ".__GLOBAL__";
// Calcluates the amount of RAM a script uses. Uses parsing and AST walking only,
// rather than NetscriptEvaluator. This is useful because NetscriptJS code does
// not work under NetscriptEvaluator.
async function parseOnlyRamCalculate(server, code, workerScript) {
try {
// Maps dependent identifiers to their dependencies.
//
// The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__.
// It depends on all the functions declared in the module, all the global scopes
// of its imports, and any identifiers referenced in this global scope. Each
// function depends on all the identifiers referenced internally.
// We walk the dependency graph to calculate RAM usage, given that some identifiers
// reference Netscript functions which have a RAM cost.
let dependencyMap = {};
// Scripts we've parsed.
const completedParses = new Set();
// Scripts we've discovered that need to be parsed.
const parseQueue = [];
// Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap.
function parseCode(code, moduleName) {
const result = parseOnlyCalculateDeps(code, moduleName);
completedParses.add(moduleName);
// Add any additional modules to the parse queue;
for (let i = 0; i < result.additionalModules.length; ++i) {
if (!completedParses.has(result.additionalModules[i])) {
parseQueue.push(result.additionalModules[i]);
}
}
// Splice all the references in.
//Spread syntax not supported in edge, use Object.assign instead
//dependencyMap = {...dependencyMap, ...result.dependencyMap};
dependencyMap = Object.assign(dependencyMap, result.dependencyMap);
}
const initialModule = "__SPECIAL_INITIAL_MODULE__";
parseCode(code, initialModule);
while (parseQueue.length > 0) {
// Get the code from the server.
const nextModule = parseQueue.shift();
let code;
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) {
try {
const module = await eval('import(nextModule)');
code = "";
for (const prop in module) {
if (typeof module[prop] === 'function') {
code += module[prop].toString() + ";\n";
}
}
} catch(e) {
console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`);
return -1;
}
} else {
const script = server.getScript(nextModule.startsWith("./") ? nextModule.slice(2) : nextModule);
if (!script) {
console.warn("Invalid script");
return -1; // No such script on the server.
}
code = script.code;
}
parseCode(code, nextModule);
}
// 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;
const unresolvedRefs = Object.keys(dependencyMap).filter(s => s.startsWith(initialModule));
const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) {
const ref = unresolvedRefs.shift();
// 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;
}
if (ref === "document" && !resolvedRefs.has("document")) {
ram += CONSTANTS.ScriptDomRamCost;
}
if (ref === "window" && !resolvedRefs.has("window")) {
ram += CONSTANTS.ScriptDomRamCost;
}
resolvedRefs.add(ref);
if (ref.endsWith(".*")) {
// A prefix reference. We need to find all matching identifiers.
const prefix = ref.slice(0, ref.length - 2);
for (let ident of Object.keys(dependencyMap).filter(k => k.startsWith(prefix))) {
for (let dep of dependencyMap[ident] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
} else {
// An exact reference. Add all dependencies of this ref.
for (let dep of dependencyMap[ref] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
// 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.
//
// TODO it would be simpler to just reference a dictionary.
try {
function applyFuncRam(func) {
if (typeof func === "function") {
try {
let res;
if (func.constructor.name === "AsyncFunction") {
res = 0; // Async functions will always be 0 RAM
} else {
res = func.apply(null, []);
}
if (typeof res === "number") {
return res;
}
return 0;
} catch(e) {
console.log("ERROR applying function: " + e);
return 0;
}
} else {
return 0;
}
}
//Special logic for namespaces (Bladeburner, CodingCOntract)
var func;
if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref];
} else if (ref in workerScript.env.vars.codingcontract) {
func = workerScript.env.vars.codingcontract[ref];
} else if (ref in workerScript.env.vars.gang) {
func = workerScript.env.vars.gang[ref];
} else {
func = workerScript.env.get(ref);
}
ram += applyFuncRam(func);
} catch (error) {continue;}
}
return ram;
} catch (error) {
// console.info("parse or eval error: ", error);
// This is not unexpected. The user may be editing a script, and it may be in
// a transitory invalid state.
return -1;
}
}
// Parses one script and calculates its ram usage, for the global scope and each function.
// Returns a cost map and a dependencyMap for the module. Returns a reference map to be joined
// onto the main reference map, and a list of modules that need to be parsed.
function parseOnlyCalculateDeps(code, currentModule) {
const ast = parse(code, {sourceType:"module", ecmaVersion: 8});
// Everything from the global scope goes in ".". Everything else goes in ".function", where only
// the outermost layer of functions counts.
const globalKey = currentModule + memCheckGlobalKey;
const dependencyMap = {};
dependencyMap[globalKey] = new Set();
// If we reference this internal name, we're really referencing that external name.
// Filled when we import names from other modules.
let internalToExternal = {};
var additionalModules = [];
// References get added pessimistically. They are added for thisModule.name, name, and for
// any aliases.
function addRef(key, name) {
const s = dependencyMap[key] || (dependencyMap[key] = new Set());
if (name in internalToExternal) {
s.add(internalToExternal[name]);
}
s.add(currentModule + "." + name);
s.add(name); // For builtins like hack.
}
//A list of identifiers that resolve to "native Javascript code"
const objectPrototypeProperties = Object.getOwnPropertyNames(Object.prototype);
// If we discover a dependency identifier, state.key is the dependent identifier.
// walkDeeper is for doing recursive walks of expressions in composites that we handle.
function commonVisitors() {
return {
Identifier: (node, st, walkDeeper) => {
if (objectPrototypeProperties.includes(node.name)) {return;}
addRef(st.key, node.name);
},
WhileStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st);
},
DoWhileStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st);
},
ForStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceFOR);
node.init && walkDeeper(node.init, st);
node.test && walkDeeper(node.test, st);
node.update && walkDeeper(node.update, st);
node.body && walkDeeper(node.body, st);
},
IfStatement: (node, st, walkDeeper) => {
addRef(st.key, specialReferenceIF);
node.test && walkDeeper(node.test, st);
node.consequent && walkDeeper(node.consequent, st);
node.alternate && walkDeeper(node.alternate, st);
},
MemberExpression: (node, st, walkDeeper) => {
node.object && walkDeeper(node.object, st);
node.property && walkDeeper(node.property, st);
},
}
}
//Spread syntax not supported in Edge yet, use Object.assign
/*
walk.recursive(ast, {key: globalKey}, {
ImportDeclaration: (node, st, walkDeeper) => {
const importModuleName = node.source.value;
additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we
// import it.
dependencyMap[st.key].add(importModuleName + memCheckGlobalKey);
for (let i = 0; i < node.specifiers.length; ++i) {
const spec = node.specifiers[i];
if (spec.imported !== undefined && spec.local !== undefined) {
// We depend on specific things.
internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name;
} else {
// We depend on everything.
dependencyMap[st.key].add(importModuleName + ".*");
}
}
},
FunctionDeclaration: (node, st, walkDeeper) => {
// Don't use walkDeeper, because we are changing the visitor set.
const key = currentModule + "." + node.id.name;
walk.recursive(node, {key: key}, commonVisitors());
},
...commonVisitors()
});
*/
walk.recursive(ast, {key: globalKey}, Object.assign({
ImportDeclaration: (node, st, walkDeeper) => {
const importModuleName = node.source.value;
additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we
// import it.
dependencyMap[st.key].add(importModuleName + memCheckGlobalKey);
for (let i = 0; i < node.specifiers.length; ++i) {
const spec = node.specifiers[i];
if (spec.imported !== undefined && spec.local !== undefined) {
// We depend on specific things.
internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name;
} else {
// We depend on everything.
dependencyMap[st.key].add(importModuleName + ".*");
}
}
},
FunctionDeclaration: (node, st, walkDeeper) => {
// Don't use walkDeeper, because we are changing the visitor set.
const key = currentModule + "." + node.id.name;
walk.recursive(node, {key: key}, commonVisitors());
},
}, commonVisitors()));
return {dependencyMap: dependencyMap, additionalModules: additionalModules};
}
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;
try {
return await parseOnlyRamCalculate(currServ, codeCopy, workerScript);
} catch (e) {
console.log("Failed to parse ram using new method. Falling back.", e);
}
// 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;
}
//Called when the game is loaded. Loads all running scripts (from all servers) //Called when the game is loaded. Loads all running scripts (from all servers)
//into worker scripts so that they will start running //into worker scripts so that they will start running
function loadAllRunningScripts() { export function loadAllRunningScripts() {
var total = 0; var total = 0;
let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1); let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1);
if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); } if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); }
@ -767,7 +359,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
var timesGrown = Math.round(0.5 * runningScriptObj.dataMap[ip][2] / runningScriptObj.onlineRunningTime * timePassed); var timesGrown = Math.round(0.5 * runningScriptObj.dataMap[ip][2] / runningScriptObj.onlineRunningTime * timePassed);
console.log(runningScriptObj.filename + " called grow() on " + serv.hostname + " " + timesGrown + " times while offline"); console.log(runningScriptObj.filename + " called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
runningScriptObj.log("Called grow() on " + serv.hostname + " " + timesGrown + " times while offline"); runningScriptObj.log("Called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
var growth = processSingleServerGrowth(serv, timesGrown * 450); var growth = processSingleServerGrowth(serv, timesGrown * 450, Player);
runningScriptObj.log(serv.hostname + " grown by " + numeralWrapper.format(growth * 100 - 100, '0.000000%') + " from grow() calls made while offline"); runningScriptObj.log(serv.hostname + " grown by " + numeralWrapper.format(growth * 100 - 100, '0.000000%') + " from grow() calls made while offline");
} }
} }
@ -838,7 +430,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
//Returns a RunningScript object matching the filename and arguments on the //Returns a RunningScript object matching the filename and arguments on the
//designated server, and false otherwise //designated server, and false otherwise
function findRunningScript(filename, args, server) { export function findRunningScript(filename, args, server) {
for (var i = 0; i < server.runningScripts.length; ++i) { for (var i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].filename == filename && if (server.runningScripts[i].filename == filename &&
compareArrays(server.runningScripts[i].args, args)) { compareArrays(server.runningScripts[i].args, args)) {
@ -847,6 +439,3 @@ function findRunningScript(filename, args, server) {
} }
return null; return null;
} }
export {loadAllRunningScripts, findRunningScript,
scriptEditorInit, isScriptFilename};

@ -0,0 +1,4 @@
// Script helper functions
export function isScriptFilename(f: string) {
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns");
}

@ -1,40 +1,61 @@
import { ipExists } from "../../utils/IPAddress"; import { Server } from "./Server";
import { SpecialServerIps } from "./SpecialServerIps";
import { serverMetadata } from "./data/servers";
import { IMap } from "../types";
import { createRandomIp,
ipExists } from "../../utils/IPAddress";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { Reviver } from "../../utils/JSONReviver";
// Map of all Servers that exist in the game // Map of all Servers that exist in the game
// Key (string) = IP // Key (string) = IP
// Value = Server object // Value = Server object
let AllServers = {}; export let AllServers: IMap<Server> = {};
// Saftely add a Server to the AllServers map // Saftely add a Server to the AllServers map
export function AddToAllServers(server) { export function AddToAllServers(server: Server): void {
var serverIp = server.ip; var serverIp = server.ip;
if (ipExists(serverIp)) { if (ipExists(serverIp)) {
console.log("IP of server that's being added: " + serverIp); console.log("IP of server that's being added: " + serverIp);
console.log("Hostname of the server thats being added: " + server.hostname); console.log("Hostname of the server thats being added: " + server.hostname);
console.log("The server that already has this IP is: " + AllServers[serverIp].hostname); console.log("The server that already has this IP is: " + AllServers[serverIp].hostname);
throw new Error("Error: Trying to add a server with an existing IP"); throw new Error("Error: Trying to add a server with an existing IP");
return;
} }
AllServers[serverIp] = server; AllServers[serverIp] = server;
} }
export function initForeignServers() { interface IServerParams {
hackDifficulty?: number;
hostname: string;
ip: string;
maxRam?: number;
moneyAvailable?: number;
numOpenPortsRequired: number;
organizationName: string;
requiredHackingSkill?: number;
serverGrowth?: number;
[key: string]: any;
}
export function initForeignServers(homeComputer: Server) {
/* Create a randomized network for all the foreign servers */ /* Create a randomized network for all the foreign servers */
//Groupings for creating a randomized network //Groupings for creating a randomized network
const networkLayers = []; const networkLayers: Server[][] = [];
for (let i = 0; i < 15; i++) { for (let i = 0; i < 15; i++) {
networkLayers.push([]); networkLayers.push([]);
} }
// Essentially any property that is of type 'number | IMinMaxRange' // Essentially any property that is of type 'number | IMinMaxRange'
const propertiesToPatternMatch = [ const propertiesToPatternMatch: string[] = [
"hackDifficulty", "hackDifficulty",
"moneyAvailable", "moneyAvailable",
"requiredHackingSkill", "requiredHackingSkill",
"serverGrowth" "serverGrowth"
]; ];
const toNumber = (value) => { const toNumber = (value: any) => {
switch (typeof value) { switch (typeof value) {
case 'number': case 'number':
return value; return value;
@ -46,7 +67,7 @@ export function initForeignServers() {
} }
for (const metadata of serverMetadata) { for (const metadata of serverMetadata) {
const serverParams = { const serverParams: IServerParams = {
hostname: metadata.hostname, hostname: metadata.hostname,
ip: createRandomIp(), ip: createRandomIp(),
numOpenPortsRequired: metadata.numOpenPortsRequired, numOpenPortsRequired: metadata.numOpenPortsRequired,
@ -79,21 +100,21 @@ export function initForeignServers() {
} }
/* Create a randomized network for all the foreign servers */ /* Create a randomized network for all the foreign servers */
const linkComputers = (server1, server2) => { const linkComputers = (server1: Server, server2: Server) => {
server1.serversOnNetwork.push(server2.ip); server1.serversOnNetwork.push(server2.ip);
server2.serversOnNetwork.push(server1.ip); server2.serversOnNetwork.push(server1.ip);
}; };
const getRandomArrayItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; const getRandomArrayItem = (arr: any[]) => arr[Math.floor(Math.random() * arr.length)];
const linkNetworkLayers = (network1, selectServer) => { const linkNetworkLayers = (network1: Server[], selectServer: () => Server) => {
for (const server of network1) { for (const server of network1) {
linkComputers(server, selectServer()); linkComputers(server, selectServer());
} }
}; };
// Connect the first tier of servers to the player's home computer // Connect the first tier of servers to the player's home computer
linkNetworkLayers(networkLayers[0], () => Player.getHomeComputer()); linkNetworkLayers(networkLayers[0], () => homeComputer);
for (let i = 1; i < networkLayers.length; i++) { for (let i = 1; i < networkLayers.length; i++) {
linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1])); linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1]));
} }
@ -106,6 +127,6 @@ export function prestigeAllServers() {
AllServers = {}; AllServers = {};
} }
export function loadAllServers(saveString) { export function loadAllServers(saveString: string) {
AllServers = JSON.parse(saveString, Reviver); AllServers = JSON.parse(saveString, Reviver);
} }

@ -1,9 +1,14 @@
// Class representing a single generic Server // Class representing a single generic Server
// TODO This import is a circular import. Try to fix it in the future
import { GetServerByHostname } from "./ServerHelpers";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CodingContract } from "../CodingContracts"; import { CodingContract } from "../CodingContracts";
import { Message } from "../Message/Message"; import { Message } from "../Message/Message";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { Script } from "../Script/Script"; import { Script } from "../Script/Script";
import { isScriptFilename } from "../Script/ScriptHelpersTS";
import { TextFile } from "../TextFile"; import { TextFile } from "../TextFile";
import { createRandomIp } from "../../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
@ -27,6 +32,11 @@ interface IConstructorParams {
} }
export class Server { export class Server {
// Initializes a Server Object from a JSON save state
static fromJSON(value: any): Server {
return Generic_fromJSON(Server, value.data);
}
// Initial server security level // Initial server security level
// (i.e. security level when the server was created) // (i.e. security level when the server was created)
baseDifficulty: number = 1; baseDifficulty: number = 1;
@ -172,49 +182,43 @@ export class Server {
this.maxRam = ram; this.maxRam = ram;
} }
//The serverOnNetwork array holds the IP of all the servers. This function // Given the name of the script, returns the corresponding
//returns the actual Server objects // script object on the server (if it exists)
Server.prototype.getServerOnNetwork = function(i) { getScript(scriptName: string): Script | null {
if (i > this.serversOnNetwork.length) { for (let i = 0; i < this.scripts.length; i++) {
console.log("Tried to get server on network that was out of range"); if (this.scripts[i].filename === scriptName) {
return;
}
return AllServers[this.serversOnNetwork[i]];
}
//Given the name of the script, returns the corresponding
//script object on the server (if it exists)
Server.prototype.getScript = function(scriptName) {
for (var i = 0; i < this.scripts.length; i++) {
if (this.scripts[i].filename == scriptName) {
return this.scripts[i]; return this.scripts[i];
} }
} }
return null; return null;
} }
Server.prototype.capDifficulty = function() { // Ensures that the server's difficulty (server security) doesn't get too high
capDifficulty(): void {
if (this.hackDifficulty < this.minDifficulty) {this.hackDifficulty = this.minDifficulty;} if (this.hackDifficulty < this.minDifficulty) {this.hackDifficulty = this.minDifficulty;}
if (this.hackDifficulty < 1) {this.hackDifficulty = 1;} if (this.hackDifficulty < 1) {this.hackDifficulty = 1;}
//Place some arbitrarily limit that realistically should never happen unless someone is
//screwing around with the game // Place some arbitrarily limit that realistically should never happen unless someone is
// screwing around with the game
if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;} if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;}
} }
//Strengthens a server's security level (difficulty) by the specified amount // Strengthens a server's security level (difficulty) by the specified amount
Server.prototype.fortify = function(amt) { fortify(amt: number): void {
this.hackDifficulty += amt; this.hackDifficulty += amt;
this.capDifficulty(); this.capDifficulty();
} }
Server.prototype.weaken = function(amt) { // Lowers the server's security level (difficulty) by the specified amount)
weaken(amt: number): void {
this.hackDifficulty -= (amt * BitNodeMultipliers.ServerWeakenRate); this.hackDifficulty -= (amt * BitNodeMultipliers.ServerWeakenRate);
this.capDifficulty(); this.capDifficulty();
} }
// Write to a script file // Write to a script file
// Overwrites existing files. Creates new files if the script does not eixst // Overwrites existing files. Creates new files if the script does not eixst
Server.prototype.writeToScriptFile = function(fn, code) { writeToScriptFile(fn: string, code: string) {
var ret = {success: false, overwritten: false}; var ret = {success: false, overwritten: false};
if (!isScriptFilename(fn)) { return ret; } if (!isScriptFilename(fn)) { return ret; }
@ -232,7 +236,7 @@ export class Server {
} }
//Otherwise, create a new script //Otherwise, create a new script
var newScript = new Script(); const newScript = new Script();
newScript.filename = fn; newScript.filename = fn;
newScript.code = code; newScript.code = code;
newScript.updateRamUsage(); newScript.updateRamUsage();
@ -244,8 +248,8 @@ export class Server {
// Write to a text file // Write to a text file
// Overwrites existing files. Creates new files if the text file does not exist // Overwrites existing files. Creates new files if the text file does not exist
Server.prototype.writeToTextFile = function(fn, txt) { writeToTextFile(fn: string, txt: string) {
var ret = {success: false, overwritten: false}; var ret = { success: false, overwritten: false };
if (!fn.endsWith("txt")) { return ret; } if (!fn.endsWith("txt")) { return ret; }
//Check if the text file already exists, and overwrite if it does //Check if the text file already exists, and overwrite if it does
@ -265,11 +269,11 @@ export class Server {
return ret; return ret;
} }
Server.prototype.addContract = function(contract) { addContract(contract: CodingContract) {
this.contracts.push(contract); this.contracts.push(contract);
} }
Server.prototype.removeContract = function(contract) { removeContract(contract: CodingContract) {
if (contract instanceof CodingContract) { if (contract instanceof CodingContract) {
this.contracts = this.contracts.filter((c) => { this.contracts = this.contracts.filter((c) => {
return c.fn !== contract.fn; return c.fn !== contract.fn;
@ -281,7 +285,7 @@ export class Server {
} }
} }
Server.prototype.getContract = function(contractName) { getContract(contractName: string) {
for (const contract of this.contracts) { for (const contract of this.contracts) {
if (contract.fn === contractName) { if (contract.fn === contractName) {
return contract; return contract;
@ -289,15 +293,11 @@ export class Server {
} }
return null; return null;
} }
}
//Functions for loading and saving a Server // Serialize the current object to a JSON save state
Server.prototype.toJSON = function() { toJSON(): any {
return Generic_toJSON("Server", this); return Generic_toJSON("Server", this);
} }
Server.fromJSON = function(value) {
return Generic_fromJSON(Server, value.data);
} }
Reviver.constructors.Server = Server; Reviver.constructors.Server = Server;

@ -1,23 +1,16 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { AllServers } from "./AllServers";
import { CodingContract, import { Server } from "./Server";
ContractTypes } from "./CodingContracts";
import { CONSTANTS } from "./Constants"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Script, import { CONSTANTS } from "../Constants";
isScriptFilename } from "./Script"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Player } from "./Player"; import { Programs } from "../Programs/Programs";
import { Programs } from "./Programs/Programs";
import { SpecialServerIps } from "./SpecialServerIps"; import {isValidIPAddress} from "../../utils/helpers/isValidIPAddress";
import { TextFile } from "./TextFile";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { serverMetadata } from "./data/servers";
import { Reviver,
Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
// Returns the number of cycles needed to grow the specified server by the // Returns the number of cycles needed to grow the specified server by the
// specified amount. 'growth' parameter is in decimal form, not percentage // specified amount. 'growth' parameter is in decimal form, not percentage
export function numCycleForGrowth(server, growth) { export function numCycleForGrowth(server: Server, growth: number, p: IPlayer) {
let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty; let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty;
if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) { if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) {
ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate; ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate;
@ -25,12 +18,12 @@ export function numCycleForGrowth(server, growth) {
const serverGrowthPercentage = server.serverGrowth / 100; const serverGrowthPercentage = server.serverGrowth / 100;
const cycles = Math.log(growth)/(Math.log(ajdGrowthRate)*Player.hacking_grow_mult*serverGrowthPercentage); const cycles = Math.log(growth)/(Math.log(ajdGrowthRate) * p.hacking_grow_mult * serverGrowthPercentage);
return cycles; return cycles;
} }
//Applied server growth for a single server. Returns the percentage growth //Applied server growth for a single server. Returns the percentage growth
export function processSingleServerGrowth(server, numCycles) { export function processSingleServerGrowth(server: Server, numCycles: number, p: IPlayer) {
//Server growth processed once every 450 game cycles //Server growth processed once every 450 game cycles
const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0); const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0);
@ -44,7 +37,7 @@ export function processSingleServerGrowth(server, numCycles) {
const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles //Apply serverGrowth for the calculated number of growth cycles
var serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * Player.hacking_grow_mult); let serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * p.hacking_grow_mult);
if (serverGrowth < 1) { if (serverGrowth < 1) {
console.log("WARN: serverGrowth calculated to be less than 1"); console.log("WARN: serverGrowth calculated to be less than 1");
serverGrowth = 1; serverGrowth = 1;
@ -66,14 +59,14 @@ export function processSingleServerGrowth(server, numCycles) {
// if there was any growth at all, increase security // if there was any growth at all, increase security
if (oldMoneyAvailable !== server.moneyAvailable) { if (oldMoneyAvailable !== server.moneyAvailable) {
//Growing increases server security twice as much as hacking //Growing increases server security twice as much as hacking
let usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable); let usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable, p);
usedCycles = Math.max(0, usedCycles); usedCycles = Math.max(0, usedCycles);
server.fortify(2 * CONSTANTS.ServerFortifyAmount * Math.ceil(usedCycles)); server.fortify(2 * CONSTANTS.ServerFortifyAmount * Math.ceil(usedCycles));
} }
return server.moneyAvailable / oldMoneyAvailable; return server.moneyAvailable / oldMoneyAvailable;
} }
export function prestigeHomeComputer(homeComp) { export function prestigeHomeComputer(homeComp: Server) {
const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name); const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name);
homeComp.programs.length = 0; //Remove programs homeComp.programs.length = 0; //Remove programs
@ -93,17 +86,9 @@ export function prestigeHomeComputer(homeComp) {
homeComp.messages.push("hackers-starting-handbook.lit"); homeComp.messages.push("hackers-starting-handbook.lit");
} }
function SizeOfAllServers() {
var size = 0, key;
for (key in AllServers) {
if (AllServers.hasOwnProperty(key)) size++;
}
return size;
}
//Returns server object with corresponding hostname //Returns server object with corresponding hostname
// Relatively slow, would rather not use this a lot // Relatively slow, would rather not use this a lot
export function GetServerByHostname(hostname) { export function GetServerByHostname(hostname: string): Server | null {
for (var ip in AllServers) { for (var ip in AllServers) {
if (AllServers.hasOwnProperty(ip)) { if (AllServers.hasOwnProperty(ip)) {
if (AllServers[ip].hostname == hostname) { if (AllServers[ip].hostname == hostname) {
@ -111,16 +96,30 @@ export function GetServerByHostname(hostname) {
} }
} }
} }
return null; return null;
} }
//Get server by IP or hostname. Returns null if invalid //Get server by IP or hostname. Returns null if invalid
export function getServer(s) { export function getServer(s: string): Server | null {
if (!isValidIPAddress(s)) { if (!isValidIPAddress(s)) {
return GetServerByHostname(s); return GetServerByHostname(s);
} }
if(AllServers[s] !== undefined) { if (AllServers[s] !== undefined) {
return AllServers[s]; return AllServers[s];
} }
return null; return null;
} }
// Returns the i-th server on the specified server's network
// A Server's serverOnNetwork property holds only the IPs. This function returns
// the actual Server object
export function getServerOnNetwork(server: Server, i: number) {
if (i > server.serversOnNetwork.length) {
console.error("Tried to get server on network that was out of range");
return;
}
return AllServers[server.serversOnNetwork[i]];
}

@ -2,16 +2,16 @@
* Implements functions for purchasing servers or purchasing more RAM for * Implements functions for purchasing servers or purchasing more RAM for
* the home computer * the home computer
*/ */
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "../Constants";
import { Player } from "./Player"; import { Player } from "../Player";
import { Server, import { AllServers } from "../Server/AllServers";
AllServers, import { Server } from "../Server/Server";
AddToAllServers} from "./Server"; import { AddToAllServers } from "../Server/ServerHelpers";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { createRandomIp } from "../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
import { yesNoTxtInpBoxGetInput } from "../utils/YesNoBox"; import { yesNoTxtInpBoxGetInput } from "../../utils/YesNoBox";
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo"; import { isPowerOfTwo } from "../../utils/helpers/isPowerOfTwo";
// Returns the cost of purchasing a server with the given RAM // Returns the cost of purchasing a server with the given RAM
// Returns Infinity for invalid 'ram' arguments // Returns Infinity for invalid 'ram' arguments

@ -1,50 +0,0 @@
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
/* Holds IP of Special Servers */
let SpecialServerNames = {
FulcrumSecretTechnologies: "Fulcrum Secret Technologies Server",
CyberSecServer: "CyberSec Server",
NiteSecServer: "NiteSec Server",
TheBlackHandServer: "The Black Hand Server",
BitRunnersServer: "BitRunners Server",
TheDarkArmyServer: "The Dark Army Server",
DaedalusServer: "Daedalus Server",
WorldDaemon: "w0r1d_d43m0n",
}
function SpecialServerIpsMap() {}
SpecialServerIpsMap.prototype.addIp = function(name, ip) {
this[name] = ip;
}
SpecialServerIpsMap.prototype.toJSON = function() {
return Generic_toJSON("SpecialServerIpsMap", this);
}
SpecialServerIpsMap.fromJSON = function(value) {
return Generic_fromJSON(SpecialServerIpsMap, value.data);
}
Reviver.constructors.SpecialServerIpsMap = SpecialServerIpsMap;
let SpecialServerIps = new SpecialServerIpsMap();
function prestigeSpecialServerIps() {
for (var member in SpecialServerIps) {
delete SpecialServerIps[member];
}
SpecialServerIps = null;
SpecialServerIps = new SpecialServerIpsMap();
}
function loadSpecialServerIps(saveString) {
SpecialServerIps = JSON.parse(saveString, Reviver);
}
function initSpecialServerIps() {
SpecialServerIps = new SpecialServerIpsMap();
}
export {SpecialServerNames, SpecialServerIps, SpecialServerIpsMap, loadSpecialServerIps,
prestigeSpecialServerIps, initSpecialServerIps};

@ -0,0 +1,56 @@
import { IMap } from "../types";
import { Reviver,
Generic_toJSON,
Generic_fromJSON } from "../../utils/JSONReviver";
/* Holds IP of Special Servers */
export let SpecialServerNames: IMap<string> = {
FulcrumSecretTechnologies: "Fulcrum Secret Technologies Server",
CyberSecServer: "CyberSec Server",
NiteSecServer: "NiteSec Server",
TheBlackHandServer: "The Black Hand Server",
BitRunnersServer: "BitRunners Server",
TheDarkArmyServer: "The Dark Army Server",
DaedalusServer: "Daedalus Server",
WorldDaemon: "w0r1d_d43m0n",
}
export class SpecialServerIpsMap {
// Initializes a SpecialServerIpsMap Object from a JSON save state
static fromJSON(value: any): SpecialServerIpsMap {
return Generic_fromJSON(SpecialServerIpsMap, value.data);
}
[key: string]: Function | string;
constructor() {}
addIp(name:string, ip: string) {
this[name] = ip;
}
// Serialize the current object to a JSON save state
toJSON(): any {
return Generic_toJSON("SpecialServerIpsMap", this);
}
}
Reviver.constructors.SpecialServerIpsMap = SpecialServerIpsMap;
export let SpecialServerIps: SpecialServerIpsMap = new SpecialServerIpsMap();
export function prestigeSpecialServerIps() {
for (var member in SpecialServerIps) {
delete SpecialServerIps[member];
}
SpecialServerIps = new SpecialServerIpsMap();
}
export function loadSpecialServerIps(saveString: string) {
SpecialServerIps = JSON.parse(saveString, Reviver);
}
export function initSpecialServerIps() {
SpecialServerIps = new SpecialServerIpsMap();
}

@ -81,6 +81,8 @@ interface IServerMetadata {
* A "unique" server that has special implications when the player manually hacks it. * A "unique" server that has special implications when the player manually hacks it.
*/ */
specialName?: string; specialName?: string;
[key: string]: any;
} }
/** /**

@ -10,8 +10,9 @@ import { executeDarkwebTerminalCommand,
checkIfConnectedToDarkweb } from "./DarkWeb/DarkWeb"; checkIfConnectedToDarkweb } from "./DarkWeb/DarkWeb";
import { DarkWebItems } from "./DarkWeb/DarkWebItems"; import { DarkWebItems } from "./DarkWeb/DarkWebItems";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {FconfSettings, parseFconfSettings, import { parseFconfSettings,
createFconf} from "./Fconf"; createFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import {calculateHackingChance, import {calculateHackingChance,
calculateHackingExpGain, calculateHackingExpGain,
calculatePercentMoneyHacked, calculatePercentMoneyHacked,
@ -22,18 +23,22 @@ import {TerminalHelpText, HelpTexts} from "./HelpText";
import {iTutorialNextStep, iTutorialSteps, import {iTutorialNextStep, iTutorialSteps,
ITutorial} from "./InteractiveTutorial"; ITutorial} from "./InteractiveTutorial";
import {showLiterature} from "./Literature"; import {showLiterature} from "./Literature";
import {showMessage, Message} from "./Message"; import { Message } from "./Message/Message";
import { showMessage } from "./Message/MessageHelpers";
import {killWorkerScript, addWorkerScript} from "./NetscriptWorker"; import {killWorkerScript, addWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player"; import {Player} from "./Player";
import {hackWorldDaemon} from "./RedPill"; import {hackWorldDaemon} from "./RedPill";
import { findRunningScript, import { RunningScript } from "./Script/RunningScript";
RunningScript, import { findRunningScript } from "./Script/ScriptHelpers";
isScriptFilename } from "./Script"; import { isScriptFilename } from "./Script/ScriptHelpersTS";
import {AllServers, GetServerByHostname, import { AllServers } from "./Server/AllServers";
getServer, Server} from "./Server"; import { Server } from "./Server/Server";
import { GetServerByHostname,
getServer,
getServerOnNetwork } from "./Server/ServerHelpers";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import {SpecialServerIps, import { SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps"; SpecialServerNames } from "./Server/SpecialServerIps";
import {getTextFile} from "./TextFile"; import {getTextFile} from "./TextFile";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {containsAllStrings, import {containsAllStrings,
@ -1157,8 +1162,8 @@ let Terminal = {
let ip = commandArray[1]; let ip = commandArray[1];
for (var i = 0; i < Player.getCurrentServer().serversOnNetwork.length; i++) { for (var i = 0; i < s.serversOnNetwork.length; i++) {
if (Player.getCurrentServer().getServerOnNetwork(i).ip == ip || Player.getCurrentServer().getServerOnNetwork(i).hostname == ip) { if (getServerOnNetwork(s, i).ip == ip || getServerOnNetwork(s, i).hostname == ip) {
Terminal.connectToServer(ip); Terminal.connectToServer(ip);
return; return;
} }
@ -1812,11 +1817,13 @@ let Terminal = {
postError("Incorrect usage of netstat/scan command. Usage: netstat/scan"); postError("Incorrect usage of netstat/scan command. Usage: netstat/scan");
return; return;
} }
//Displays available network connections using TCP
// Displays available network connections using TCP
const currServ = Player.getCurrentServer();
post("Hostname IP Root Access"); post("Hostname IP Root Access");
for (let i = 0; i < Player.getCurrentServer().serversOnNetwork.length; i++) { for (let i = 0; i < currServ.serversOnNetwork.length; i++) {
//Add hostname //Add hostname
let entry = Player.getCurrentServer().getServerOnNetwork(i); let entry = getServerOnNetwork(currServ, i);
if (entry == null) { continue; } if (entry == null) { continue; }
entry = entry.hostname; entry = entry.hostname;
@ -1824,16 +1831,16 @@ let Terminal = {
let numSpaces = 21 - entry.length; let numSpaces = 21 - entry.length;
let spaces = Array(numSpaces+1).join(" "); let spaces = Array(numSpaces+1).join(" ");
entry += spaces; entry += spaces;
entry += Player.getCurrentServer().getServerOnNetwork(i).ip; entry += getServerOnNetwork(currServ, i).ip;
//Calculate padding and add root access info //Calculate padding and add root access info
let hasRoot; let hasRoot;
if (Player.getCurrentServer().getServerOnNetwork(i).hasAdminRights) { if (getServerOnNetwork(currServ, i).hasAdminRights) {
hasRoot = 'Y'; hasRoot = 'Y';
} else { } else {
hasRoot = 'N'; hasRoot = 'N';
} }
numSpaces = 21 - Player.getCurrentServer().getServerOnNetwork(i).ip.length; numSpaces = 21 - getServerOnNetwork(currServ, i).ip.length;
spaces = Array(numSpaces+1).join(" "); spaces = Array(numSpaces+1).join(" ");
entry += spaces; entry += spaces;
entry += hasRoot; entry += hasRoot;
@ -1867,7 +1874,7 @@ let Terminal = {
visited[s.ip] = 1; visited[s.ip] = 1;
} }
for (var i = s.serversOnNetwork.length-1; i >= 0; --i) { for (var i = s.serversOnNetwork.length-1; i >= 0; --i) {
stack.push(s.getServerOnNetwork(i)); stack.push(getServerOnNetwork(s, i));
depthQueue.push(d+1); depthQueue.push(d+1);
} }
if (d == 0) {continue;} //Don't print current server if (d == 0) {continue;} //Don't print current server

@ -21,14 +21,12 @@ import {CompanyPositions} from "./Company/CompanyP
import {initCompanies} from "./Company/Companies"; import {initCompanies} from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation"; import { Corporation } from "./Corporation/Corporation";
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "./Constants";
import {createDevMenu, closeDevMenu} from "./DevMenu"; import {createDevMenu, closeDevMenu} from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions"; import { Factions, initFactions } from "./Faction/Factions";
import { displayFactionContent, joinFaction, import { displayFactionContent, joinFaction,
processPassiveFactionRepGain, processPassiveFactionRepGain,
inviteToFaction } from "./Faction/FactionHelpers"; inviteToFaction } from "./Faction/FactionHelpers";
import {FconfSettings} from "./Fconf"; import { FconfSettings } from "./Fconf/FconfSettings";
import {displayLocationContent, import {displayLocationContent,
initLocationButtons} from "./Location"; initLocationButtons} from "./Location";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
@ -36,7 +34,7 @@ import {displayHacknetNodesContent, processAllHacknetNodeEarnings,
updateHacknetNodesContent} from "./HacknetNode"; updateHacknetNodesContent} from "./HacknetNode";
import {iTutorialStart} from "./InteractiveTutorial"; import {iTutorialStart} from "./InteractiveTutorial";
import {initLiterature} from "./Literature"; import {initLiterature} from "./Literature";
import {checkForMessagesToSend, initMessages} from "./Message"; import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import {inMission, currMission} from "./Missions"; import {inMission, currMission} from "./Missions";
import {initSingularitySFFlags, import {initSingularitySFFlags,
hasSingularitySF, hasCorporationSF} from "./NetscriptFunctions"; hasSingularitySF, hasCorporationSF} from "./NetscriptFunctions";
@ -54,13 +52,14 @@ import {saveObject, loadGame} from "./SaveObject";
import { getCurrentEditor, import { getCurrentEditor,
loadAllRunningScripts, loadAllRunningScripts,
scriptEditorInit, scriptEditorInit,
updateScriptEditorContent } from "./Script"; updateScriptEditorContent } from "./Script/ScriptHelpers";
import {AllServers, Server, initForeignServers} from "./Server"; import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { initForeignServers } from "./Server/ServerHelpers";
import {Settings} from "./Settings/Settings"; import {Settings} from "./Settings/Settings";
import { initSourceFiles, SourceFiles } from "./SourceFile"; import { initSourceFiles, SourceFiles } from "./SourceFile";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerIps, initSpecialServerIps} from "./Server/SpecialServerIps";
import {SpecialServerIps, initSpecialServerIps} from "./SpecialServerIps";
import {StockMarket, StockSymbols, import {StockMarket, StockSymbols,
SymbolToStockMap, initStockSymbols, SymbolToStockMap, initStockSymbols,
initSymbolToStockMap, stockMarketCycle, initSymbolToStockMap, stockMarketCycle,
@ -1317,7 +1316,7 @@ const Engine = {
Engine.setDisplayElements(); //Sets variables for important DOM elements Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.start(); //Run main game loop and Scripts loop Engine.start(); //Run main game loop and Scripts loop
Player.init(); Player.init();
initForeignServers(); initForeignServers(Player.getHomeComputer());
initCompanies(); initCompanies();
initFactions(); initFactions();
initAugmentations(); initAugmentations();

@ -1,5 +1,5 @@
import {AllServers} from "../src/Server"; import { AllServers } from "../src/Server/AllServers";
import {getRandomByte} from "./helpers/getRandomByte"; import { getRandomByte } from "./helpers/getRandomByte";
/* Functions to deal with manipulating IP addresses*/ /* Functions to deal with manipulating IP addresses*/