/* Evaluator * Evaluates the Abstract Syntax Tree for Netscript * generated by the Parser class */ // Evaluator should return a Promise, so that any call to evaluate() can just //wait for that promise to finish before continuing function evaluate(exp, workerScript) { return new Promise(function(resolve, reject) { var env = workerScript.env; if (env.stopFlag) {return reject(workerScript);} if (exp == null) { return reject(makeRuntimeRejectMsg(workerScript, "Error: NULL expression")); } setTimeout(function() { if (env.stopFlag) {return reject(workerScript);} switch (exp.type) { case "num": case "str": case "bool": resolve(exp.value); break; case "var": if (exp.value == "hacknetnodes") { var pEvaluateHacknetNode = evaluateHacknetNode(exp, workerScript); pEvaluateHacknetNode.then(function(res) { resolve(res); }, function(e) { reject(e); }); return; } else if (exp.value == "array") { //A raw array. This will be called under something like this: // x = Array[1, 2, 3]; if (exp.array && exp.array instanceof Array) { resolve(exp.array); } else { reject(makeRuntimeRejectMsg(workerScript, "Invalid array instantiation")); } return; } try { var res = env.get(exp.value); if (exp.index) { //If theres an index field, then this variable is supposed to be an array //and the user needs to be indexing it if (res.constructor === Array || res instanceof Array) { var iPromise = evaluate(exp.index.value, workerScript); iPromise.then(function(i) { if (i >= res.length || i < 0) { return reject(makeRuntimeRejectMsg(workerScript, "Out of bounds: Invalid index in [] operator")); } else { return evaluate(res[i], workerScript); } }).then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); } else { reject(makeRuntimeRejectMsg(workerScript, "Trying to access a non-array variable using the [] operator")); } } else { resolve(res); } } catch (e) { reject("|" + workerScript.serverIp + "|" + workerScript.name + "|" + e.toString()); } break; //Can currently only assign to "var"s case "assign": var p = netscriptAssign(exp, workerScript); p.then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); break; case "binary": var p = netscriptBinary(exp, workerScript); p.then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); break; case "if": var numConds = exp.cond.length; var numThens = exp.then.length; if (numConds == 0 || numThens == 0 || numConds != numThens) { reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Number of conds and thens in if structure don't match (or there are none)"); } var evalIfPromise = evaluateIf(exp, workerScript, 0); evalIfPromise.then(function(res) { if (res) { //One of the if/elif statements evaluated to true resolve("if statement done"); } else { //None of the if/elif statements were true. Evaluate else if there is one if (exp.else) { var elseEval = evaluate(exp.else, workerScript); elseEval.then(function(res) { resolve("if statement done with else"); }, function(e) { reject(e); }); } else { resolve("if statement done"); } } }, function(e) { reject(e); }); break; case "for": var pInit = evaluate(exp.init, workerScript); pInit.then(function(expInit) { return evaluateFor(exp, workerScript); }).then(function(forLoopRes) { resolve("forLoopDone"); }).catch(function(e) { reject(e); }); break; case "while": var pEvaluateWhile = evaluateWhile(exp, workerScript); pEvaluateWhile.then(function(whileLoopRes) { resolve(whileLoopRes); }, function(e) { reject(e); }); break; case "prog": var evaluateProgPromise = evaluateProg(exp, workerScript, 0); evaluateProgPromise.then(function(w) { resolve(workerScript); }, function(e) { if (typeof e === 'string' || e instanceof String) { workerScript.errorMessage = e; reject(workerScript); } else if (e instanceof WorkerScript) { reject(e); } else { reject(workerScript); } }); break; case "call": if (exp.func.value == "hack") { var p = netscriptHack(exp, workerScript); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "sleep") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "sleep() call has incorrect number of arguments. Takes 1 argument.")); } var sleepTimePromise = evaluate(exp.args[0], workerScript); sleepTimePromise.then(function(sleepTime) { workerScript.scriptRef.log("Sleeping for " + sleepTime + " milliseconds"); return netscriptDelay(sleepTime).then(function() { return Promise.resolve(true); }); }).then(function(res) { resolve(true); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "print") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "print() call has incorrect number of arguments. Takes 1 argument")); } var evaluatePromise = evaluate(exp.args[0], workerScript); evaluatePromise.then(function(res) { workerScript.scriptRef.log(res.toString()); resolve(true); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "grow") { var p = netscriptGrow(exp, workerScript); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "weaken") { var p = netscriptWeaken(exp, workerScript); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "nuke") { var p = netscriptRunProgram(exp, workerScript, Programs.NukeProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "brutessh") { var p = netscriptRunProgram(exp, workerScript, Programs.BruteSSHProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "ftpcrack") { var p = netscriptRunProgram(exp, workerScript, Programs.FTPCrackProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "relaysmtp") { var p = netscriptRunProgram(exp, workerScript, Programs.RelaySMTPProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "httpworm") { var p = netscriptRunProgram(exp, workerScript, Programs.HTTPWormProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "sqlinject") { var p = netscriptRunProgram(exp, workerScript, Programs.SQLInjectProgram); p.then(function(res) { resolve(res); }, function(e) { reject(e); }); } else if (exp.func.value == "hasRootAccess") { if (env.stopFlag) {return reject(workerScript);} if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "hasRootAccess() call has incorrect number of arguments. Takes 1 argument")); } var ipPromise = evaluate(exp.args[0], workerScript); ipPromise.then(function(ip) { if (env.stopFlag) {return reject(workerScript);} var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("hasRootAccess() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into hasRootAccess() command")); } workerScript.scriptRef.log("hasRootAccess() returned " + server.hasAdminRights); resolve(server.hasAdminRights); }, function(e) { reject(e); }); } else if (exp.func.value == "run") { if (exp.args.length != 1 && exp.args.length != 2) { return reject(makeRuntimeRejectMsg(workerScript, "run() call has incorrect number of arguments. Takes 1 or 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} var scriptname = args[0]; var threads = 1; if (exp.args.length == 2) { threads = args[1]; } if (isNaN(threads) || threads < 1) { return reject(makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into run(). Must be numeric and greater than 0")); } var scriptServer = getServer(workerScript.serverIp); if (scriptServer == null) { return reject(makeRuntimeRejectMsg(workerScript, "Could not find server. This is a bug in the game. Report to game dev")); } var runScriptPromise = runScriptFromScript(scriptServer, scriptname, workerScript, threads); return runScriptPromise; }).then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "exec") { if (exp.args.length != 2 && exp.args.length != 3) { return reject(makeRuntimeRejectMsg(workerScript, "exec() call has incorrect number of arguments. Takes 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} var threads = 1; if (exp.args.length == 3) { threads = args[2]; } if (isNaN(threads) || threads < 1) { return reject(makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into exec(). Must be numeric and greater than 0")); } var server = getServer(args[1]); if (server == null) { return reject(makeRuntimeRejectMsg(workerScript, "Invalid hostname/ip passed into exec() command: " + args[1])); } return runScriptFromScript(server, args[0], workerScript, threads); }).then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "kill") { if (exp.args.length != 1 && exp.args.length != 2) { return reject(makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Takes 1 or 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); var filename = ""; Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} filename = args[0]; if (exp.args.length == 1) { return Promise.resolve(workerScript.serverIp); } else { return evaluate(exp.args[1], workerScript); } }).then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("kill() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into kill() command")); } var res = killWorkerScript(filename, server.ip); if (res) { workerScript.scriptRef.log("Killing " + filename + ". May take up to a few minutes for the scripts to die..."); return Promise.resolve(true); } else { workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname); return Promise.resolve(false); } }).then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "killall") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "killall() call has incorrect number of arguments. Takes 1 argument")); } var ipPromise = evaluate(exp.args[0], workerScript); ipPromise.then(function(ip) { if (env.stopFlag) {return reject(workerScript);} var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("killall() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into killall() command")); } workerScript.scriptRef.log("killall(): Killing all scripts on " + server.hostname + ". May take a few minutes for the scripts to die"); for (var i = server.runningScripts.length; i >= 0; --i) { killWorkerScript(server.runningScripts[i], server.ip); } resolve(true); }, function(e) { reject(e); }); } else if (exp.func.value == "scp") { if (exp.args.length != 2) { return reject(makeRuntimeRejectMsg(workerScript, "scp() call has incorrect number of arguments. Takes 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} var scriptname = args[0]; var ip = args[1]; var destServer = getServer(ip); if (destServer == null) { return reject(makeRuntimeRejectMsg(workerScript, "Invalid hostname/ip passed into scp() command: " + ip)); } var currServ = getServer(workerScript.serverIp); if (currServ == null) { return reject(makeRuntimeRejectMsg(workerScript, "Could not find server ip for this script. This is a bug please contact game developer")); } var sourceScript = null; for (var i = 0; i < currServ.scripts.length; ++i) { if (scriptname == currServ.scripts[i].filename) { sourceScript = currServ.scripts[i]; break; } } if (sourceScript == null) { workerScript.scriptRef.log(scriptname + " does not exist. scp() failed"); return Promise.resolve(false); } //Overwrite script if it already exists for (var i = 0; i < destServer.scripts.length; ++i) { if (scriptname == destServer.scripts[i].filename) { workerScript.scriptRef.log("WARNING: " + scriptname + " already exists on " + destServer.hostname + " and it will be overwritten."); workerScript.scriptRef.log(scriptname + " overwritten on " + destServer.hostname); var oldScript = destServer.scripts[i]; oldScript.code = sourceScript.code; oldScript.ramUsage = sourceScript.ramUsage; return Promise.resolve(true); } } //Create new script if it does not already exist var newScript = new Script(); newScript.filename = scriptname; newScript.code = sourceScript.code; newScript.ramUsage = sourceScript.ramUsage; newScript.server = destServer.ip; destServer.scripts.push(newScript); workerScript.scriptRef.log(scriptname + " copied over to " + destServer.hostname); return Promise.resolve(true); return; }).then(function(res) { resolve(res); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "getHostname") { if (exp.args.length != 0) { return reject(makeRuntimeRejectMsg(workerScript, "getHostname() call has incorrect number of arguments. Takes 0 arguments")); } var scriptServer = getServer(workerScript.serverIp); if (scriptServer == null) { return reject(makeRuntimeRejectMsg(workerScript, "Could not find server. This is a bug in the game. Report to game dev")); } resolve(scriptServer.hostname); } else if (exp.func.value == "getHackingLevel") { if (exp.args.length != 0) { return reject(makeRuntimeRejectMsg(workerScript, "getHackingLevel() call has incorrect number of arguments. Takes 0 arguments")); } Player.updateSkillLevels(); workerScript.scriptRef.log("getHackingLevel() returned " + Player.hacking_skill); resolve(Player.hacking_skill); } else if (exp.func.value == "getServerMoneyAvailable") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "getServerMoneyAvailable() call has incorrect number of arguments. Takes 1 arguments")); } var ipPromise = evaluate(exp.args[0], workerScript); ipPromise.then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("Cannot getServerMoneyAvailable(). Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into getServerMoneyAvailable() command")); } workerScript.scriptRef.log("getServerMoneyAvailable() returned " + formatNumber(server.moneyAvailable, 2) + " for " + server.hostname); resolve(server.moneyAvailable); }, function(e) { reject(e); }); } else if (exp.func.value == "getServerSecurityLevel") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "getServerSecurityLevel() call has incorrect number of arguments. Takes 1 arguments")); } var ipPromise = evaluate(exp.args[0], workerScript); ipPromise.then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getServerSecurityLevel() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into getServerSecurityLevel() command"));; } workerScript.scriptRef.log("getServerSecurityLevel() returned " + formatNumber(server.hackDifficulty, 3) + " for " + server.hostname); resolve(server.hackDifficulty); }, function(e) { reject(e); }); } else if (exp.func.value == "getServerRequiredHackingLevel") { if (exp.args.length != 1) { return reject(makeRuntimeRejectMsg(workerScript, "getServerRequiredHackingLevel() call has incorrect number of arguments. Takes 1 argument")); } var ipPromise = evaluate(exp.args[0], workerScript); ipPromise.then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("getServerRequiredHackingLevel() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into getServerRequiredHackingLevel() command")); } workerScript.scriptRef.log("getServerRequiredHackingLevel returned " + formatNumber(server.requiredHackingSkill, 0) + " for " + server.hostname); resolve(server.requiredHackingSkill); }, function(e) { reject(e); }); } else if (exp.func.value == "fileExists") { if (exp.args.length != 1 && exp.args.length != 2) { return reject(makeRuntimeRejectMsg(workerScript, "fileExists() call has incorrect number of arguments. Takes 1 or 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); var filename = ""; Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} filename = args[0]; if (exp.args.length == 1) { return Promise.resolve(workerScript.serverIp); } else { return evaluate(exp.args[1], workerScript); } }).then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("fileExists() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into fileExists() command")); } for (var i = 0; i < server.scripts.length; ++i) { if (filename == server.scripts[i].filename) { return resolve(true); } } if (Player.hasProgram(filename)) { return resolve(true); } return resolve(false); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "isRunning") { if (exp.args.length != 1 && exp.args.length != 2) { return reject(makeRuntimeRejectMsg(workerScript, "isRunning() call has incorrect number of arguments. Takes 1 or 2 arguments")); } var argPromises = exp.args.map(function(arg) { return evaluate(arg, workerScript); }); var filename = ""; Promise.all(argPromises).then(function(args) { if (env.stopFlag) {return reject(workerScript);} filename = args[0]; if (exp.args.length == 1) { return Promise.resolve(workerScript.serverIp); } else { return evaluate(exp.args[1], workerScript); } }).then(function(ip) { var server = getServer(ip); if (server == null) { workerScript.scriptRef.log("isRunning() failed. Invalid IP or hostname passed in: " + ip); return reject(makeRuntimeRejectMsg(workerScript, "Invalid IP or hostname passed into isRunning() command")); } for (var i = 0; i < server.runningScripts.length; ++i) { if (filename == server.runningScripts[i]) { return resolve(true); } } return resolve(false); }).catch(function(e) { reject(e); }); } else if (exp.func.value == "purchaseHacknetNode") { if (exp.args.length != 0) { return reject(makeRuntimeRejectMsg(workerScript, "purchaseHacknetNode() call has incorrect number of arguments. Takes 0 arguments")); } var cost = getCostOfNextHacknetNode(); if (isNaN(cost)) { return reject(makeRuntimeRejectMsg(workerScript, "Could not calculate cost in purchaseHacknetNode(). This is a bug please report to game dev")); } if (cost > Player.money) { workerScript.scriptRef.log("Could not afford to purchase new Hacknet Node"); return resolve(false); } //Auto generate a name for the node for now...TODO var numOwned = Player.hacknetNodes.length; var name = "hacknet-node-" + numOwned; var node = new HacknetNode(name); node.updateMoneyGainRate(); Player.loseMoney(cost); Player.hacknetNodes.push(node); displayHacknetNodesContent(); workerScript.scriptRef.log("Purchased new Hacknet Node with name: " + name); resolve(numOwned); } else { reject(makeRuntimeRejectMsg(workerScript, "Invalid function: " + exp.func.value)); } break; default: reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". This is a bug please report to game developer")); break; } //End switch }, CONSTANTS.CodeInstructionRunTime); //End setTimeout, the Netscript operation run time }); // End Promise } //Returns true if any of the if statements evaluated, false otherwise. Therefore, the else statement //should evaluate if this returns false function evaluateIf(exp, workerScript, i) { var env = workerScript.env; return new Promise(function(resolve, reject) { if (i >= exp.cond.length) { //Catch out of bounds errors resolve(false); } else { var cond = evaluate(exp.cond[i], workerScript); cond.then(function(condRes) { if (condRes) { var evalThen = evaluate(exp.then[i], workerScript); evalThen.then(function(res) { resolve(true); }, function(e) { reject(e); }); } else { //If this if statement isnt true, go on the next elif, or recursively resolve if (i == exp.cond.length-1) { resolve(false); } else { var recursiveCall = evaluateIf(exp, workerScript, i+1); recursiveCall.then(function(res) { resolve(res); }, function(e) { reject(e); }); } } }, function(e) { reject(e); }); } }); } //Evaluate the looping part of a for loop (Initialization block is NOT done in here) function evaluateFor(exp, workerScript) { var env = workerScript.env; return new Promise(function(resolve, reject) { if (env.stopFlag) {reject(workerScript); return;} var pCond = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.cond, workerScript); evaluatePromise.then(function(resCond) { resolve(resCond); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); pCond.then(function(resCond) { if (resCond) { //Run the for loop code var pCode = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.code, workerScript); evaluatePromise.then(function(resCode) { resolve(resCode); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); //After the code executes make a recursive call pCode.then(function(resCode) { var pPostLoop = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.postloop, workerScript); evaluatePromise.then(function(foo) { resolve("postLoopFinished"); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); pPostLoop.then(function(resPostloop) { var recursiveCall = evaluateFor(exp, workerScript); recursiveCall.then(function(foo) { resolve("endForLoop"); }, function(e) { reject(e); }); }, function(e) { reject(e); }); }, function(e) { reject(e); }); } else { resolve("endForLoop"); //Doesn't need to resolve to any particular value } }, function(e) { reject(e); }); }); } function evaluateWhile(exp, workerScript) { var env = workerScript.env; return new Promise(function(resolve, reject) { if (env.stopFlag) {reject(workerScript); return;} var pCond = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.cond, workerScript); evaluatePromise.then(function(resCond) { resolve(resCond); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); pCond.then(function(resCond) { if (resCond) { //Run the while loop code var pCode = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.code, workerScript); evaluatePromise.then(function(resCode) { resolve(resCode); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); //After the code executes make a recursive call pCode.then(function(resCode) { var recursiveCall = evaluateWhile(exp, workerScript); recursiveCall.then(function(foo) { resolve("endWhileLoop"); }, function(e) { reject(e); }); }, function(e) { reject(e); }); } else { resolve("endWhileLoop"); //Doesn't need to resolve to any particular value } }, function(e) { reject(e); }); }); } function evaluateHacknetNode(exp, workerScript) { var env = workerScript.env; return new Promise(function(resolve, reject) { setTimeout(function() { if (exp.index == null) { if ((exp.op.type == "call" && exp.op.value == "length") || (exp.op.type == "var" && exp.op.value == "length")) { resolve(Player.hacknetNodes.length); workerScript.scriptRef.log("hacknetnodes.length returned " + Player.hacknetNodes.length); return; } else { workerScript.scriptRef.log("Invalid/null index for hacknetnodes"); reject(makeRuntimeRejectMsg(workerScript, "Invalid/null index. hacknetnodes array must be accessed with an index")); return; } } var indexPromise = evaluate(exp.index.value, workerScript); indexPromise.then(function(index) { if (isNaN(index) || index >= Player.hacknetNodes.length || index < 0) { workerScript.scriptRef.log("Invalid index value for hacknetnodes[]"); reject(makeRuntimeRejectMsg(workerScript, "Invalid index value for hacknetnodes[].")); return; } var nodeObj = Player.hacknetNodes[index]; if (exp.op == null) { reject(makeRuntimeRejectMsg(workerScript, "No operator or property called for hacknetnodes. Usage: hacknetnodes[i].property/operator")); return; } else if (exp.op.type == "var") { //Get properties: level, ram, cores switch(exp.op.value) { case "level": resolve(nodeObj.level); break; case "ram": resolve(nodeObj.ram); break; case "cores": resolve(nodeObj.numCores); break; default: reject(makeRuntimeRejectMsg(workerScript, "Unrecognized property for Hacknet Node. Valid properties: ram, cores, level")); break; } } else if (exp.op.type == "call") { switch(exp.op.func.value) { case "upgradeLevel": if (exp.op.args.length == 1) { var argPromise = evaluate(exp.op.args[0], workerScript); argPromise.then(function(arg) { if (isNaN(arg) || arg < 0) { reject(makeRuntimeRejectMsg(workerScript, "Invalid argument passed into upgradeLevel()")); return; } arg = Math.round(arg); var res = nodeObj.purchaseLevelUpgrade(arg); if (res) { workerScript.scriptRef.log("Upgraded " + nodeObj.name + " " + arg + " times to level " + nodeObj.level); } resolve(res); }, function(e) { reject(e); }); } else { var res = nodeObj.purchaseLevelUpgrade(1); if (res) { workerScript.scriptRef.log("Upgraded " + nodeObj.name + " once to level " + nodeObj.level); } resolve(res); } break; case "upgradeRam": var res = nodeObj.purchaseRamUpgrade(); if (res) { workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s RAM to " + nodeObj.ram + "GB"); } resolve(res); break; case "upgradeCore": var res = nodeObj.purchaseCoreUpgrade(); if (res) { workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s number of cores to " + nodeObj.numCores); } resolve(res); break; default: reject(makeRuntimeRejectMsg(workerScript, "Unrecognized function/operator for hacknet node. Valid functions: upgradeLevel(n), upgradeRam(), upgradeCore()")); break; } } else { reject(makeRuntimeRejectMsg(workerScript, "Unrecognized operation for hacknet node")); return; } }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }, function(e) { reject(e); }); } function evaluateProg(exp, workerScript, index) { var env = workerScript.env; return new Promise(function(resolve, reject) { if (env.stopFlag) {reject(workerScript); return;} if (index >= exp.prog.length) { resolve("progFinished"); } else { //Evaluate this line of code in the prog var code = new Promise(function(resolve, reject) { setTimeout(function() { var evaluatePromise = evaluate(exp.prog[index], workerScript); evaluatePromise.then(function(evalRes) { resolve(evalRes); }, function(e) { reject(e); }); }, CONSTANTS.CodeInstructionRunTime); }); //After the code finishes evaluating, evaluate the next line recursively code.then(function(codeRes) { var nextLine = evaluateProg(exp, workerScript, index + 1); nextLine.then(function(nextLineRes) { resolve(workerScript); }, function(e) { reject(e); }); }, function(e) { reject(e); }); } }); } function netscriptDelay(time) { return new Promise(function(resolve) { setTimeout(resolve, time); }); } function makeRuntimeRejectMsg(workerScript, msg) { return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg; } function apply_op(op, a, b) { function num(x) { if (typeof x != "number") throw new Error("Expected number but got " + x); return x; } function div(x) { if (num(x) == 0) throw new Error("Divide by zero"); return x; } switch (op) { case "+": return a + b; case "-": return num(a) - num(b); case "*": return num(a) * num(b); case "/": return num(a) / div(b); case "%": return num(a) % div(b); case "&&": return a !== false && b; case "||": return a !== false ? a : b; case "<": return num(a) < num(b); case ">": return num(a) > num(b); case "<=": return num(a) <= num(b); case ">=": return num(a) >= num(b); case "==": return a === b; case "!=": return a !== b; } throw new Error("Can't apply operator " + op); } //Run a script from inside a script using run() command function runScriptFromScript(server, scriptname, workerScript, threads=1) { return new Promise(function(resolve, reject) { var env = workerScript.env; if (env.stopFlag) {reject(workerScript); return;} setTimeout(function() { //Check if the script is already running for (var i = 0; i < server.runningScripts.length; ++i) { if (server.runningScripts[i] == scriptname) { workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname); resolve(false); return; } } //Check if the script exists and if it does run it for (var i = 0; i < server.scripts.length; ++i) { if (server.scripts[i].filename == scriptname) { //Check for admin rights and that there is enough RAM availble to run var ramUsage = server.scripts[i].ramUsage; ramUsage = ramUsage * threads * Math.pow(1.02, threads-1); var ramAvailable = server.maxRam - server.ramUsed; if (server.hasAdminRights == false) { workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!"); resolve(false); return; } else if (ramUsage > ramAvailable){ workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!"); resolve(false); return; } else { //Able to run script workerScript.scriptRef.log("Running script: " + scriptname + " on " + server.hostname + " with " + threads + " threads. May take a few seconds to start up..."); var script = server.scripts[i]; script.threads = threads; server.runningScripts.push(script.filename); //Push onto runningScripts addWorkerScript(script, server); resolve(true); return; } } } workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname); resolve(false); }, CONSTANTS.CodeInstructionRunTime); }); } function isScriptErrorMessage(msg) { splitMsg = msg.split("|"); if (splitMsg.length != 4){ return false; } var ip = splitMsg[1]; if (!isValidIPAddress(ip)) { return false; } return true; } //The same as Player's calculateHackingChance() function but takes in the server as an argument function scriptCalculateHackingChance(server) { var difficultyMult = (100 - server.hackDifficulty) / 100; var skillMult = (2 * Player.hacking_skill); var skillChance = (skillMult - server.requiredHackingSkill) / skillMult; var chance = skillChance * difficultyMult * Player.hacking_chance_mult; if (chance < 0) {return 0;} else {return chance;} } //The same as Player's calculateHackingTime() function but takes in the server as an argument function scriptCalculateHackingTime(server) { var difficultyMult = server.requiredHackingSkill * server.hackDifficulty; var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50); var hackingTime = skillFactor * Player.hacking_speed_mult * 5; //This is in seconds return hackingTime; } //The same as Player's calculateExpGain() function but takes in the server as an argument function scriptCalculateExpGain(server) { return (server.hackDifficulty * Player.hacking_exp_mult * 0.9); } //The same as Player's calculatePercentMoneyHacked() function but takes in the server as an argument function scriptCalculatePercentMoneyHacked(server) { var difficultyMult = (100 - server.hackDifficulty) / 100; var skillMult = (Player.hacking_skill - (server.requiredHackingSkill - 1)) / Player.hacking_skill; var percentMoneyHacked = difficultyMult * skillMult * Player.hacking_money_mult / 200; if (percentMoneyHacked < 0) {return 0;} if (percentMoneyHacked > 1) {return 1;} return percentMoneyHacked; } //Amount of time to execute grow() in milliseconds function scriptCalculateGrowTime(server) { var difficultyMult = server.requiredHackingSkill * server.hackDifficulty; var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50); var growTime = skillFactor * Player.hacking_speed_mult * 16; //This is in seconds return growTime * 1000; } //Amount of time to execute weaken() in milliseconds function scriptCalculateWeakenTime(server) { var difficultyMult = server.requiredHackingSkill * server.hackDifficulty; var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50); var weakenTime = skillFactor * Player.hacking_speed_mult * 40; //This is in seconds return weakenTime * 1000; }