mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-09-22 05:40:57 +02:00
865 lines
35 KiB
JavaScript
865 lines
35 KiB
JavaScript
import {BitNodeMultipliers} from "./BitNode.js";
|
|
import {CONSTANTS} from "./Constants.js";
|
|
import {Player} from "./Player.js";
|
|
import {Environment} from "./NetscriptEnvironment.js";
|
|
import {WorkerScript, addWorkerScript} from "./NetscriptWorker.js";
|
|
import {Server} from "./Server.js";
|
|
import {Settings} from "./Settings.js";
|
|
import {Script, findRunningScript,
|
|
RunningScript} from "./Script.js";
|
|
|
|
import {Node} from "../utils/acorn.js";
|
|
import {printArray} from "../utils/HelperFunctions.js";
|
|
import {isValidIPAddress} from "../utils/IPAddress.js";
|
|
import {isString} from "../utils/StringHelperFunctions.js";
|
|
|
|
/* Evaluator
|
|
* Evaluates/Interprets the Abstract Syntax Tree generated by Acorns parser
|
|
*
|
|
* Returns a promise
|
|
*/
|
|
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 "BlockStatement":
|
|
case "Program":
|
|
var evaluateProgPromise = evaluateProg(exp, workerScript, 0); //TODO: make every block/program use individual enviroment
|
|
evaluateProgPromise.then(function(w) {
|
|
resolve(workerScript);
|
|
}, function(e) {
|
|
if (e.constructor === Array && e.length === 2 && e[0] === "RETURNSTATEMENT") {
|
|
//Returning from a Player-defined function
|
|
resolve(e[1]);
|
|
} else if (isString(e)) {
|
|
workerScript.errorMessage = e;
|
|
reject(workerScript);
|
|
} else if (e instanceof WorkerScript) {
|
|
reject(e);
|
|
} else {
|
|
reject(workerScript);
|
|
}
|
|
});
|
|
break;
|
|
case "Literal":
|
|
resolve(exp.value);
|
|
break;
|
|
case "Identifier":
|
|
if (!(exp.name in env.vars)){
|
|
reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.name + " not defined"));
|
|
}
|
|
resolve(env.get(exp.name))
|
|
break;
|
|
case "ExpressionStatement":
|
|
var e = evaluate(exp.expression, workerScript);
|
|
e.then(function(res) {
|
|
resolve(res);
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "ArrayExpression":
|
|
var argPromises = exp.elements.map(function(arg) {
|
|
return evaluate(arg, workerScript);
|
|
});
|
|
Promise.all(argPromises).then(function(array) {
|
|
resolve(array)
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "CallExpression":
|
|
evaluate(exp.callee, workerScript).then(function(func) {
|
|
var argPromises = exp.arguments.map(function(arg) {
|
|
return evaluate(arg, workerScript);
|
|
});
|
|
Promise.all(argPromises).then(function(args) {
|
|
if (func instanceof Node) { //Player-defined function
|
|
//Create new Environment for the function
|
|
//Should be automatically garbage collected...
|
|
var funcEnv = env.extend();
|
|
console.log("Printing new environment for function:");
|
|
console.log(funcEnv);
|
|
|
|
//Define function arguments in this new environment
|
|
for (var i = 0; i < func.params.length; ++i) {
|
|
var arg;
|
|
if (i >= args.length) {
|
|
arg = null;
|
|
} else {
|
|
arg = args[i];
|
|
}
|
|
funcEnv.def(func.params[i].name, arg);
|
|
}
|
|
|
|
//Create a new WorkerScript for this function evaluation
|
|
var funcWorkerScript = new WorkerScript(workerScript.scriptRef);
|
|
funcWorkerScript.env = funcEnv;
|
|
|
|
evaluate(func.body, funcWorkerScript).then(function(res) {
|
|
resolve(res);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
} else if (exp.callee.type == "MemberExpression"){
|
|
evaluate(exp.callee.object, workerScript).then(function(object) {
|
|
try {
|
|
var res = func.apply(object,args);
|
|
resolve(res);
|
|
} catch (e) {
|
|
reject(makeRuntimeRejectMsg(workerScript, e));
|
|
}
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
} else {
|
|
try {
|
|
var out = func.apply(null,args);
|
|
if (out instanceof Promise){
|
|
out.then(function(res) {
|
|
resolve(res)
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
} else {
|
|
resolve(out);
|
|
}
|
|
} catch (e) {
|
|
if (isScriptErrorMessage(e)) {
|
|
reject(e);
|
|
} else {
|
|
reject(makeRuntimeRejectMsg(workerScript, e));
|
|
}
|
|
}
|
|
}
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "MemberExpression":
|
|
var pObject = evaluate(exp.object, workerScript);
|
|
pObject.then(function(object) {
|
|
if (exp.computed){
|
|
var p = evaluate(exp.property, workerScript);
|
|
p.then(function(index) {
|
|
if (index >= object.length) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Invalid index for arrays"));
|
|
}
|
|
resolve(object[index]);
|
|
}).catch(function(e) {
|
|
reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression"));
|
|
});
|
|
} else {
|
|
try {
|
|
resolve(object[exp.property.name])
|
|
} catch (e) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString()));
|
|
}
|
|
}
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "LogicalExpression":
|
|
case "BinaryExpression":
|
|
var p = evalBinary(exp, workerScript, resolve, reject);
|
|
p.then(function(res) {
|
|
resolve(res);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "UnaryExpression":
|
|
var p = evalUnary(exp, workerScript, resolve, reject);
|
|
p.then(function(res) {
|
|
resolve(res);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "AssignmentExpression":
|
|
var p = evalAssignment(exp, workerScript);
|
|
p.then(function(res) {
|
|
resolve(res);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "UpdateExpression":
|
|
if (exp.argument.type==="Identifier"){
|
|
if (exp.argument.name in env.vars){
|
|
if (exp.prefix){
|
|
resolve(env.get(exp.argument.name))
|
|
}
|
|
switch (exp.operator){
|
|
case "++":
|
|
env.set(exp.argument.name,env.get(exp.argument.name)+1);
|
|
break;
|
|
case "--":
|
|
env.set(exp.argument.name,env.get(exp.argument.name)-1);
|
|
break;
|
|
default:
|
|
reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". You are trying to use code that is currently unsupported"));
|
|
}
|
|
if (env.prefix){
|
|
return;
|
|
}
|
|
resolve(env.get(exp.argument.name))
|
|
} else {
|
|
reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.argument.name + " not defined"));
|
|
}
|
|
} else {
|
|
reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier"));
|
|
}
|
|
break;
|
|
case "EmptyStatement":
|
|
resolve(false);
|
|
break;
|
|
case "ReturnStatement":
|
|
console.log("Evaluating Return Statement");
|
|
//var lineNum = getErrorLineNumber(exp, workerScript);
|
|
//reject(makeRuntimeRejectMsg(workerScript, "Return statements are not yet implemented in Netscript (line " + (lineNum+1) + ")"));
|
|
evaluate(exp.argument, workerScript).then(function(res) {
|
|
reject(["RETURNSTATEMENT", res]);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "BreakStatement":
|
|
reject("BREAKSTATEMENT");
|
|
break;
|
|
case "ContinueStatement":
|
|
reject("CONTINUESTATEMENT");
|
|
break;
|
|
case "IfStatement":
|
|
evaluateIf(exp, workerScript).then(function(forLoopRes) {
|
|
resolve(forLoopRes);
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
break;
|
|
case "SwitchStatement":
|
|
var lineNum = getErrorLineNumber(exp, workerScript);
|
|
reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript (line " + (lineNum+1) + ")"));
|
|
break;e
|
|
case "WhileStatement":
|
|
evaluateWhile(exp, workerScript).then(function(forLoopRes) {
|
|
resolve(forLoopRes);
|
|
}).catch(function(e) {
|
|
if (e == "BREAKSTATEMENT" ||
|
|
(e instanceof WorkerScript && e.errorMessage == "BREAKSTATEMENT")) {
|
|
return resolve("whileLoopBroken");
|
|
} else {
|
|
reject(e);
|
|
}
|
|
});
|
|
break;
|
|
case "ForStatement":
|
|
evaluate(exp.init, workerScript).then(function(expInit) {
|
|
return evaluateFor(exp, workerScript);
|
|
}).then(function(forLoopRes) {
|
|
resolve("forLoopDone");
|
|
}).catch(function(e) {
|
|
if (e == "BREAKSTATEMENT" ||
|
|
(e instanceof WorkerScript && e.errorMessage == "BREAKSTATEMENT")) {
|
|
return resolve("forLoopBroken");
|
|
} else {
|
|
reject(e);
|
|
}
|
|
});
|
|
break;
|
|
case "FunctionDeclaration":
|
|
if (exp.id && exp.id.name) {
|
|
env.set(exp.id.name, exp);
|
|
resolve(true);
|
|
} else {
|
|
var lineNum = getErrorLineNumber(exp, workerScript);
|
|
reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration at line " + lineNum+1));
|
|
}
|
|
break;
|
|
default:
|
|
var lineNum = getErrorLineNumber(exp, workerScript);
|
|
reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + " (line " + (lineNum+1) + "). This is currently unsupported in Netscript"));
|
|
break;
|
|
} //End switch
|
|
}, Settings.CodeInstructionRunTime); //End setTimeout, the Netscript operation run time
|
|
|
|
}); // End Promise
|
|
}
|
|
|
|
function evalBinary(exp, workerScript){
|
|
return new Promise(function(resolve, reject) {
|
|
var expLeftPromise = evaluate(exp.left, workerScript);
|
|
expLeftPromise.then(function(expLeft) {
|
|
if (expLeft == true && exp.operator === "||") {
|
|
return resolve(true);
|
|
}
|
|
if (expLeft == false && exp.operator === "&&") {
|
|
return resolve(false);
|
|
}
|
|
var expRightPromise = evaluate(exp.right, workerScript);
|
|
expRightPromise.then(function(expRight) {
|
|
switch (exp.operator){
|
|
case "===":
|
|
case "==":
|
|
resolve(expLeft===expRight);
|
|
break;
|
|
case "!==":
|
|
case "!=":
|
|
resolve(expLeft!==expRight);
|
|
break;
|
|
case "<":
|
|
resolve(expLeft<expRight);
|
|
break;
|
|
case "<=":
|
|
resolve(expLeft<=expRight);
|
|
break;
|
|
case ">":
|
|
resolve(expLeft>expRight);
|
|
break;
|
|
case ">=":
|
|
resolve(expLeft>=expRight);
|
|
break;
|
|
case "+":
|
|
resolve(expLeft+expRight);
|
|
break;
|
|
case "-":
|
|
resolve(expLeft-expRight);
|
|
break;
|
|
case "*":
|
|
resolve(expLeft*expRight);
|
|
break;
|
|
case "/":
|
|
if (expRight === 0) {
|
|
reject(makeRuntimeRejectMsg(workerScript, "ERROR: Divide by zero"));
|
|
} else {
|
|
resolve(expLeft/expRight);
|
|
}
|
|
break;
|
|
case "%":
|
|
resolve(expLeft%expRight);
|
|
break;
|
|
case "in":
|
|
resolve(expLeft in expRight);
|
|
break;
|
|
case "instanceof":
|
|
resolve(expLeft instanceof expRight);
|
|
break;
|
|
case "||":
|
|
resolve(expLeft || expRight);
|
|
break;
|
|
case "&&":
|
|
resolve(expLeft && expRight);
|
|
break;
|
|
default:
|
|
reject(makeRuntimeRejectMsg(workerScript, "Unsupported operator: " + exp.operator));
|
|
}
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
function evalUnary(exp, workerScript){
|
|
var env = workerScript.env;
|
|
return new Promise(function(resolve, reject) {
|
|
if (env.stopFlag) {return reject(workerScript);}
|
|
var p = evaluate(exp.argument, workerScript);
|
|
p.then(function(res) {
|
|
if (exp.operator == "!") {
|
|
resolve(!res);
|
|
} else if (exp.operator == "-") {
|
|
if (isNaN(res)) {
|
|
resolve(res);
|
|
} else {
|
|
resolve(-1 * res);
|
|
}
|
|
} else {
|
|
reject(makeRuntimeRejectMsg(workerScript, "Unimplemented unary operator: " + exp.operator));
|
|
}
|
|
}).catch(function(e) {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
//Takes in a MemberExpression that should represent a Netscript array (possible multidimensional)
|
|
//The return value is an array of the form:
|
|
// [0th index (leftmost), array name, 1st index, 2nd index, ...]
|
|
function getArrayElement(exp, workerScript) {
|
|
return new Promise(function(resolve, reject) {
|
|
var indices = [];
|
|
var iPromise = evaluate(exp.property, workerScript);
|
|
iPromise.then(function(idx) {
|
|
if (isNaN(idx)) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Invalid access to array. Index is not a number: " + idx));
|
|
} else {
|
|
if (exp.object.name === undefined && exp.object.object) {
|
|
var recursePromise = getArrayElement(exp.object, workerScript);
|
|
recursePromise.then(function(res) {
|
|
res.push(idx);
|
|
indices = res;
|
|
return resolve(indices);
|
|
}).catch(function(e) {
|
|
return reject(e);
|
|
});
|
|
} else {
|
|
indices.push(idx);
|
|
indices.push(exp.object.name);
|
|
return resolve(indices);
|
|
}
|
|
}
|
|
}).catch(function(e) {
|
|
console.log(e);
|
|
console.log("Error getting index in getArrayElement: " + e.toString());
|
|
return reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
function evalAssignment(exp, workerScript) {
|
|
var env = workerScript.env;
|
|
return new Promise(function(resolve, reject) {
|
|
if (env.stopFlag) {return reject(workerScript);}
|
|
|
|
if (exp.left.type != "Identifier" && exp.left.type != "MemberExpression") {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Cannot assign to " + JSON.stringify(exp.left)));
|
|
}
|
|
|
|
if (exp.operator !== "=" && !(exp.left.name in env.vars)){
|
|
return reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.left.name + " not defined"));
|
|
}
|
|
|
|
var expRightPromise = evaluate(exp.right, workerScript);
|
|
expRightPromise.then(function(expRight) {
|
|
if (exp.left.type == "MemberExpression") {
|
|
//Assign to array element
|
|
//Array object designed by exp.left.object.name
|
|
//Index designated by exp.left.property
|
|
var getArrayElementPromise = getArrayElement(exp.left, workerScript);
|
|
getArrayElementPromise.then(function(res) {
|
|
if (!(res instanceof Array) || res.length < 2) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Error evaluating array assignment. This is (probably) a bug please report to game dev"));
|
|
}
|
|
|
|
//The array name is the second value
|
|
var arrName = res.splice(1, 1);
|
|
arrName = arrName[0];
|
|
|
|
env.setArrayElement(arrName, res, expRight);
|
|
return resolve(false);
|
|
}).catch(function(e) {
|
|
return reject(e);
|
|
});
|
|
/*
|
|
var name = exp.left.object.name;
|
|
if (!(name in env.vars)){
|
|
reject(makeRuntimeRejectMsg(workerScript, "variable " + name + " not defined"));
|
|
}
|
|
var arr = env.get(name);
|
|
if (arr.constructor === Array || arr instanceof Array) {
|
|
var iPromise = evaluate(exp.left.property, workerScript);
|
|
iPromise.then(function(idx) {
|
|
if (isNaN(idx)) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Invalid access to array. Index is not a number: " + idx));
|
|
} else if (idx >= arr.length || idx < 0) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Out of bounds: Invalid index in [] operator"));
|
|
} else {
|
|
env.setArrayElement(name, idx, expRight);
|
|
}
|
|
}).catch(function(e) {
|
|
return reject(e);
|
|
});
|
|
} else {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Trying to access a non-array variable using the [] operator"));
|
|
}*/
|
|
} else {
|
|
//Other assignments
|
|
try {
|
|
switch (exp.operator) {
|
|
case "=":
|
|
env.set(exp.left.name,expRight);
|
|
break;
|
|
case "+=":
|
|
env.set(exp.left.name,env.get(exp.left.name) + expRight);
|
|
break;
|
|
case "-=":
|
|
env.set(exp.left.name,env.get(exp.left.name) - expRight);
|
|
break;
|
|
case "*=":
|
|
env.set(exp.left.name,env.get(exp.left.name) * expRight);
|
|
break;
|
|
case "/=":
|
|
env.set(exp.left.name,env.get(exp.left.name) / expRight);
|
|
break;
|
|
case "%=":
|
|
env.set(exp.left.name,env.get(exp.left.name) % expRight);
|
|
break;
|
|
default:
|
|
reject(makeRuntimeRejectMsg(workerScript, "Bitwise assignment is not implemented"));
|
|
}
|
|
resolve(false);
|
|
} catch (e) {
|
|
return reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString()));
|
|
}
|
|
}
|
|
//resolve(false); //Return false so this doesnt cause conditionals to evaluate
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
function evaluateIf(exp, workerScript, i) {
|
|
var env = workerScript.env;
|
|
return new Promise(function(resolve, reject) {
|
|
evaluate(exp.test, workerScript).then(function(condRes) {
|
|
if (condRes) {
|
|
evaluate(exp.consequent, workerScript).then(function(res) {
|
|
resolve(true);
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
} else if (exp.alternate) {
|
|
evaluate(exp.alternate, workerScript).then(function(res) {
|
|
resolve(true);
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
} else {
|
|
resolve("endIf")
|
|
}
|
|
}, 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 = evaluate(exp.test, workerScript);
|
|
pCond.then(function(resCond) {
|
|
if (resCond) {
|
|
//Run the for loop code
|
|
var pBody = evaluate(exp.body, workerScript);
|
|
//After the code executes make a recursive call
|
|
pBody.then(function(resCode) {
|
|
var pUpdate = evaluate(exp.update, workerScript);
|
|
pUpdate.then(function(resPostloop) {
|
|
var recursiveCall = evaluateFor(exp, workerScript);
|
|
recursiveCall.then(function(foo) {
|
|
resolve("endForLoop");
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
}, function(e) {
|
|
if (e == "CONTINUESTATEMENT" ||
|
|
(e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) {
|
|
//Continue statement, recurse to next iteration
|
|
var pUpdate = evaluate(exp.update, workerScript);
|
|
pUpdate.then(function(resPostloop) {
|
|
var recursiveCall = evaluateFor(exp, workerScript);
|
|
recursiveCall.then(function(foo) {
|
|
resolve("endForLoop");
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
} else {
|
|
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.test, 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.body, workerScript);
|
|
evaluatePromise.then(function(resCode) {
|
|
resolve(resCode);
|
|
}, function(e) {
|
|
if (e == "CONTINUESTATEMENT" ||
|
|
(e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) {
|
|
//Continue statement, recurse
|
|
var recursiveCall = evaluateWhile(exp, workerScript);
|
|
recursiveCall.then(function(foo) {
|
|
resolve("endWhileLoop");
|
|
}, function(e) {
|
|
reject(e);
|
|
});
|
|
} else {
|
|
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 evaluateProg(exp, workerScript, index) {
|
|
var env = workerScript.env;
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
if (env.stopFlag) {reject(workerScript); return;}
|
|
|
|
if (index >= exp.body.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.body[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, workerScript) {
|
|
return new Promise(function(resolve) {
|
|
var delay = setTimeout(resolve, time);
|
|
workerScript.killTrigger = function() {
|
|
clearTimeout(delay);
|
|
resolve();
|
|
};
|
|
});
|
|
}
|
|
|
|
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, args, workerScript, threads=1) {
|
|
//Check if the script is already running
|
|
var runningScriptObj = findRunningScript(scriptname, args, server);
|
|
if (runningScriptObj != null) {
|
|
workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname);
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
//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 script = server.scripts[i];
|
|
var ramUsage = script.ramUsage;
|
|
threads = Math.round(Number(threads)); //Convert to number and round
|
|
ramUsage = ramUsage * threads * Math.pow(CONSTANTS.MultithreadingRAMCost, 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!");
|
|
return Promise.resolve(false);
|
|
} else if (ramUsage > ramAvailable){
|
|
workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!");
|
|
return Promise.resolve(false);
|
|
} else {
|
|
//Able to run script
|
|
workerScript.scriptRef.log("Running script: " + scriptname + " on " + server.hostname + " with " + threads + " threads and args: " + printArray(args) + ". May take a few seconds to start up...");
|
|
var runningScriptObj = new RunningScript(script, args);
|
|
runningScriptObj.threads = threads;
|
|
server.runningScripts.push(runningScriptObj); //Push onto runningScripts
|
|
addWorkerScript(runningScriptObj, server);
|
|
return Promise.resolve(true);
|
|
}
|
|
}
|
|
}
|
|
workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname);
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
//Takes in a
|
|
function getErrorLineNumber(exp, workerScript) {
|
|
var code = workerScript.scriptRef.scriptRef.code;
|
|
|
|
//Split code up to the start of the node
|
|
code = code.substring(0, exp.start);
|
|
return (code.match(/\n/g) || []).length;
|
|
}
|
|
|
|
function isScriptErrorMessage(msg) {
|
|
if (!isString(msg)) {return false;}
|
|
let 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 = (1.75 * Player.hacking_skill) + (0.2 * Player.intelligence);
|
|
var skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
|
|
var chance = skillChance * difficultyMult * Player.hacking_chance_mult;
|
|
if (chance > 1) {return 1;}
|
|
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 + (0.1 * Player.intelligence));
|
|
var hackingTime = 5 * skillFactor / Player.hacking_speed_mult; //This is in seconds
|
|
return hackingTime;
|
|
}
|
|
|
|
//The same as Player's calculateExpGain() function but takes in the server as an argument
|
|
function scriptCalculateExpGain(server) {
|
|
if (server.baseDifficulty == null) {
|
|
server.baseDifficulty = server.hackDifficulty;
|
|
}
|
|
return (server.baseDifficulty * Player.hacking_exp_mult * 0.3 + 3) * BitNodeMultipliers.HackExpGain;
|
|
}
|
|
|
|
//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 / 240;
|
|
if (percentMoneyHacked < 0) {return 0;}
|
|
if (percentMoneyHacked > 1) {return 1;}
|
|
return percentMoneyHacked * BitNodeMultipliers.ScriptHackMoney;
|
|
}
|
|
|
|
//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 + (0.1 * Player.intelligence));
|
|
var growTime = 16 * skillFactor / Player.hacking_speed_mult; //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 + (0.1 * Player.intelligence));
|
|
var weakenTime = 20 * skillFactor / Player.hacking_speed_mult; //This is in seconds
|
|
return weakenTime * 1000;
|
|
}
|
|
|
|
export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
|
|
scriptCalculateHackingChance, scriptCalculateHackingTime,
|
|
scriptCalculateExpGain, scriptCalculatePercentMoneyHacked,
|
|
scriptCalculateGrowTime, scriptCalculateWeakenTime, evaluate,
|
|
isScriptErrorMessage};
|