Got NetScript working asyhcronously with Promises! Only for loops have been tested though. Still need to test while loops and ifs. While loops should work if the for loops do, idk about if statements though. If statements aren't a high priority right now though

This commit is contained in:
Daniel Xie 2016-11-29 16:56:05 -06:00
parent 1efee3b7d6
commit 77f0c31777
4 changed files with 241 additions and 332 deletions

@ -2,4 +2,7 @@ CONSTANTS = {
//Max level for any skill. Determined by max numerical value in javascript and the skill level
//formula in Player.js
MaxSkillLevel: 1796,
//Time (ms) it takes to run one operation in Netscript.
CodeInstructionRunTime: 1500,
}

@ -7,53 +7,76 @@
* 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) {
var env = workerScript.env;
switch (exp.type) {
case "num":
case "str":
case "bool":
return exp.value;
return new Promise(function(resolve, reject) {
resolve(exp.value);
});
break;
case "var":
return env.get(exp.value);
return new Promise(function(resolve, reject) {
resolve(env.get(exp.value));
});
break;
//Can currently only assign to "var"s
case "assign":
console.log("Evaluating assign operation");
return new Promise(function(resolve, reject) {
if (exp.left.type != "var")
throw new Error("Cannot assign to " + JSON.stringify(exp.left));
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
var res = evaluate(exp.right, workerScript);
resolve(res);
}, 3000)
var expRightPromise = evaluate(exp.right, workerScript);
expRightPromise.then(function(expRight) {
resolve(expRight);
});
}, CONSTANTS.CodeInstructionRunTime)
});
return p.then(function(expRight) {
return env.set(exp.left.value, expRight);
p.then(function(expRight) {
console.log("Right side of assign operation resolved with value: " + expRight);
env.set(exp.left.value, expRight);
console.log("Assign operation finished");
resolve("assignFinished");
});
});
case "binary":
console.log("Binary operation called");
return new Promise(function(resolve, reject) {
var pLeft = new Promise(function(resolve, reject) {
setTimeout(function() {
var resLeft = evaluate(exp.left, workerScript);
resolve(resLeft);
}, 3000);
var promise = evaluate(exp.left, workerScript);
promise.then(function(valLeft) {
resolve(valLeft);
});
}, CONSTANTS.CodeInstructionRunTime);
});
var pRight = pLeft.then(function(expLeft) {
pLeft.then(function(valLeft) {
var pRight = new Promise(function(resolve, reject) {
setTimeout(function() {
var resRight = evaluate(exp.right, workerScript);
resolve([expLeft, resRight]);
}, 3000);
var promise = evaluate(exp.right, workerScript);
promise.then(function(valRight) {
resolve([valLeft, valRight]);
});
}, CONSTANTS.CodeInstructionRunTime);
});
return pRight.then(function(args) {
return apply_op(exp.operator,
args[0],
args[1]);
pRight.then(function(args) {
console.log("Resolving binary operation");
resolve(apply_op(exp.operator, args[0], args[1]));
});
});
});
break;
//TODO
case "if":
@ -73,98 +96,42 @@ function evaluate(exp, workerScript) {
return exp.else ? evaluate(exp.else, workerScript) : false;
case "for":
return new Promise(function(resolve, reject) {
console.log("for loop encountered in evaluator");
var pInit = new Promise(function(resolve, reject) {
setTimeout(function() {
var resInit = evaluate(exp.init, workerScript);
resInit.then(function(foo) {
resolve(resInit);
}, 3000);
});
}, CONSTANTS.CodeInstructionRunTime);
});
pInit.then(function(expInit) {
setTimeout(function() {
var resCond = evaluate(exp.cond, workerScript);
console.log("Evaluated the conditional");
resolve(resCond);
}, 3000);
})
.then(function(expCond) {
while (expCond) {
var pCode = new Promise(function(resolve, reject) {
setTimeout(function() {
var resCode = evaluate(exp.code, workerScript);
resolve(resCode);
}, 3000);
var pForLoop = evaluateFor(exp, workerScript);
pForLoop.then(function(forLoopRes) {
resolve("forLoopDone");
});
var pPostloop = pCode.then(function(expCode) {
setTimeout(function() {
var resPostloop = evaluate(exp.postloop, workerScript);
resolve(resPostloop);
}, 3000);
});
var pCond = pPostloop.then(function(expPostloop) {
setTimeout(function() {
var resCond = evaluate(exp.cond, workerScript);
resolve(resCond);
}, 3000);
});
pCond.then(function(resCond) {
expCond = resCond;
//Do i need resolve here?
});
}
});
//TODO I don't think I need to return anything..but I might be wrong
break;
case "while":
var pCond = new Promise(function(resolve, reject) {
setTimeout(function() {
var resCond = evaluate(exp.cond, workerScript);
resolve(resCond);
}, 3000);
console.log("Evaluating while loop");
return new Promise(function(resolve, reject) {
var pEvaluateWhile = evaluateWhile(exp, workerScript);
pEvaluateWhile.then(function(whileLoopRes) {
resolve("whileLoopDone");
});
pCond.then(function(expCond) {
while (expCond) {
var pCode = new Promise(function(resolve, reject) {
setTimeout(function() {
var resCode = evaluate(exp.code, workerScript);
resolve(resCode);
}, 3000);
});
pCode.then(function(expCode) {
expCond = evaluate(exp.cond, workerScript);
//Do i need resolve here?
});
}
});
//TODO I don't think I need to return anything..but I might be wrong
break;
case "prog":
var val = false;
exp.prog.forEach(function(exp){
var pExp = new Promise(function(resolve, reject) {
setTimeout(function() {
var resExp = evaluate(exp, workerScript);
resolve(resExp);
}, 3000);
});
pExp.then(function(resExp) {
val = resExp;
return new Promise(function(resolve, reject) {
var evaluateProgPromise = evaluateProg(exp, workerScript, 0);
evaluateProgPromise.then(function(res) {
resolve(res);
});
});
return val;
break;
/* Currently supported function calls:
* hack()
@ -178,13 +145,32 @@ function evaluate(exp, workerScript) {
//return func.apply(null, exp.args.map(function(arg){
// return evaluate(arg, env);
//}));
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (exp.func.value == "hack") {
console.log("Execute hack()");
resolve("hackExecuted");
} else if (exp.func.value == "sleep") {
console.log("Execute sleep()");
resolve("sleepExecuted");
} else if (exp.func.value == "print") {
post(evaluate(exp.args[0], workerScript).toString());
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
var evaluatePromise = evaluate(exp.args[0], workerScript);
evaluatePromise.then(function(res) {
resolve(res);
});
}, CONSTANTS.CodeInstructionRunTime);
});
p.then(function(res) {
post(res.toString());
console.log("Print call executed");
resolve("printExecuted");
});
}
}, CONSTANTS.CodeInstructionRunTime);
});
break;
default:
@ -192,6 +178,131 @@ function evaluate(exp, workerScript) {
}
}
//Evaluate the looping part of a for loop (Initialization block is NOT done in here)
function evaluateFor(exp, workerScript) {
console.log("evaluateFor() called");
return new Promise(function(resolve, reject) {
var pCond = new Promise(function(resolve, reject) {
setTimeout(function() {
var evaluatePromise = evaluate(exp.cond, workerScript);
evaluatePromise.then(function(resCond) {
console.log("Conditional evaluated to: " + resCond);
resolve(resCond);
});
}, CONSTANTS.CodeInstructionRunTime);
});
pCond.then(function(resCond) {
if (resCond) {
console.log("About to evaluate an iteration of for loop code");
//Run the for loop code
var pCode = new Promise(function(resolve, reject) {
setTimeout(function() {
var evaluatePromise = evaluate(exp.code, workerScript);
evaluatePromise.then(function(resCode) {
console.log("Evaluated an iteration of for loop code");
resolve(resCode);
});
}, 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) {
console.log("Evaluated for loop postloop");
resolve("postLoopFinished");
});
}, CONSTANTS.CodeInstructionRunTime);
});
pPostLoop.then(function(resPostloop) {
var recursiveCall = evaluateFor(exp, workerScript);
recursiveCall.then(function(foo) {
resolve("endForLoop");
});
});
});
} else {
console.log("Cond is false, stopping for loop");
resolve("endForLoop"); //Doesn't need to resolve to any particular value
}
});
});
}
function evaluateWhile(exp, workerScript) {
console.log("evaluateWhile() called");
return new Promise(function(resolve, reject) {
var pCond = new Promise(function(resolve, reject) {
setTimeout(function() {
var evaluatePromise = evaluate(exp.cond, workerScript);
evaluatePromise.then(function(resCond) {
console.log("Conditional evaluated to: " + resCond);
resolve(resCond);
});
}, 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) {
console.log("Evaluated an iteration of while loop code");
resolve(resCode);
});
}, 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");
});
});
} else {
console.log("Cond is false, stopping while loop");
resolve("endWhileLoop"); //Doesn't need to resolve to any particular value
}
});
});
}
function evaluateProg(exp, workerScript, index) {
console.log("evaluateProg() called");
return new Promise(function(resolve, reject) {
if (index >= exp.prog.length) {
console.log("Prog done. Resolving recursively");
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);
});
}, 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("progDone");
});
});
}
});
}
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
@ -726,207 +837,20 @@ function InputStream(input) {
}
}
/* Object that defines the performance/statistics of the script,
* such as how much money it has stolen from every Foreign Server
* and how much exp it has gained for the player.
*
* Every NetscriptWorker has its own ScriptStats object that is passed
* back to the main thread regularly. The main thread then uses the information
* from the object to change the game state. Then, the worker resets the Script Stats
* object and continues. */
function ScriptStats() {
ECorpMoneyHacked = 0;
ECorpTimesHacked = 0;
MegaCorpMoneyHacked = 0;
MegaCorpTimesHacked = 0;
BachmanAndAssociatesMoneyHacked = 0;
BachmanAndAssociatesTimesHacked = 0;
NWOMoneyHacked = 0;
NWOTimesHacked = 0;
ClarkeIncorporatedMoneyHacked = 0;
ClarkeIncorporatedTimesHacked = 0;
OmniTekIncorporatedMoneyHacked = 0;
OmniTekIncorporatedTimesHacked = 0;
FourSigmaMoneyHacked = 0;
FourSigmaTimesHacked = 0;
KuaiGongInternationalMoneyHacked = 0;
KuaiGongInternationalTimesHacked = 0;
FulcrumTechnologiesMoneyHacked = 0;
FulcrumTechnologiesTimesHacked = 0;
FulcrumSecretTechnologiesMoneyHacked = 0;
FulcrumSecretTechnologiesTimesHacked = 0;
StormTechnologiesMoneyHacked = 0;
StormTechnologiesTimesHacked = 0;
DefCommMoneyHacked = 0;
DefCommTimesHacked = 0;
InfoCommMoneyHacked = 0;
InfoCommTimesHacked = 0;
HeliosLabsMoneyHacked = 0;
HeliosLabsTimesHacked = 0;
VitaLifeMoneyHacked = 0;
VitaLifeTimesHacked = 0;
IcarusMicrosystemsMoneyHacked = 0;
IcarusMicrosystemsTimesHacked = 0;
UniversalEnergyMoneyHacked = 0;
UniversalEnergyTimesHacked = 0;
TitanLabsMoneyHacked = 0;
TitanLabsTimesHacked = 0;
MicrodyneTechnologiesMoneyHacked = 0;
MicrodyneTechnologiesTimesHacked = 0;
TaiYangDigitalMoneyHacked = 0;
TaiYangDigitalTimesHacked = 0;
GalacticCybersystemsMoneyHacked = 0;
GalacticCybersystemsTimesHacked = 0;
AeroCorpMoneyHacked = 0;
AeroCorpTimesHacked = 0;
OmniaCybersystemsMoneyHacked = 0;
OmniaCybersystemsTimesHacked = 0;
ZBDefenseMoneyHacked = 0;
ZBDefenseTimesHacked = 0;
AppliedEnergeticsMoneyHacked = 0;
AppliedEnergeticsTimesHacked = 0;
SolarisSpaceSystemsMoneyHacked = 0;
SolarisSpaceSystemsTimesHacked = 0;
DeltaOneMoneyHacked = 0;
DeltaOneTimesHacked = 0;
GlobalPharmaceuticalsMoneyHacked = 0;
GlobalPharmaceuticalsTimesHacked = 0;
NovaMedicalMoneyHacked = 0;
NovaMedicalTimesHacked = 0;
ZeusMedicalMoneyHacked = 0;
ZeusMedicalTimesHacked = 0;
UnitaLifeGroupMoneyHacked = 0;
UnitaLifeGroupTimesHacked = 0;
LexoCorpMoneyHacked = 0;
LexoCorpTimesHacked = 0;
RhoConstructionMoneyHacked = 0;
RhoConstructionTimesHacked = 0;
AlphaEnterprisesMoneyHacked = 0;
AlphaEnterprisesTimesHacked = 0;
AevumPoliceMoneyHacked = 0;
AevumPoliceTimesHacked = 0;
RothmanUniversityMoneyHacked = 0;
RothmanUniversityTimesHacked = 0;
ZBInstituteOfTechnologyMoneyHacked = 0;
ZBInstituteOfTechnologyTimesHacked = 0;
SummitUniversityMoneyHacked = 0;
SummitUniversityTimesHacked = 0;
SysCoreSecuritiesMoneyHacked = 0;
SysCoreSecuritiesTimesHacked = 0;
CatalystVenturesMoneyHacked = 0;
CatalystVenturesTimesHacked = 0;
TheHubMoneyHacked = 0;
TheHubTimesHacked = 0;
CompuTekMoneyHacked = 0;
CompuTekTimesHacked = 0;
NetLinkTechnologiesMoneyHacked = 0;
NetLinkTechnologiesTimesHacked = 0;
JohnsonOrthopedicsMoneyHacked = 0;
JohnsonOrthopedicsTimesHacked = 0;
FoodNStuffMoneyHacked = 0;
FoodNStuffTimesHacked = 0;
SigmaCosmeticsMoneyHacked = 0;
SigmaCosmeticsTimesHacked = 0;
JoesGunsMoneyHacked = 0;
JoesGunsTimesHacked = 0;
Zer0NightclubMoneyHacked = 0;
Zer0NightclubTimesHacked = 0;
NectarNightclubMoneyHacked = 0;
NectarNightclubTimesHacked = 0;
NeoNightclubMoneyHacked = 0;
NeoNightclubTimesHacked = 0;
SilverHelixMoneyHacked = 0;
SilverHelixTimesHacked = 0;
HongFangTeaHouseMoneyHacked = 0;
HongFangTeaHouseTimesHacked = 0;
HaraKiriSushiBarMoneyHacked = 0;
HaraKiriSushiBarTimesHacked = 0;
PhantasyMoneyHacked = 0;
PhantasyTimesHacked = 0;
MaxHardwareMoneyHacked = 0;
MaxHardwareTimesHacked = 0;
OmegaSoftwareMoneyHacked = 0;
OmegaSoftwareTimesHacked = 0;
CrushFitnessGymMoneyHacked = 0;
CrushFitnessGymTimesHacked = 0;
IronGymMoneyHacked = 0;
IronGymTimesHacked = 0;
MilleniumFitnessGymMoneyHacked = 0;
MilleniumFitnessGymTimesHacked = 0;
PowerhouseGymMoneyHacked = 0;
PowerhouseGymTimesHacked = 0;
SnapFitnessGymMoneyHacked = 0;
SnapFitnessGymTimesHacked = 0;
}
ScriptStats.prototype.reset = function() {
for (var key in this) {
if (this.hasOwnProperty(key)) {
this[key] = 0;
}
}
}
/* Actual Worker Code */
function WorkerScript() {
this.name = "";
this.running = false;
this.server = null;
this.code = "";
this.env = new Environment();
this.timeout = null;
}
var scriptStats = new ScriptStats();
var workerForeignServers = null;
var workerPlayer = null;
var workerScripts = [];
self.addEventListener('message', msgRecvHandler);
function msgRecvHandler(e) {
/* The first element of the data array from main thread specifies
* what kind of data it is:
* Status Update
* Start Script
* Stop Script
*/
if (e.data.type == "Status Update") {
console.log("Status update received in Script Web Worker");
ForeignServersScript = JSON.parse(e.data.buf1);
PlayerScript = JSON.parse(e.data.buf2);
} else if (e.data.type == "Start Script") {
var s = new WorkerScript();
s.name = e.data.buf1;
s.code = e.data.buf2;
workerScripts.push(s);
} else if (e.data.type == "Stop Script") {
var scriptName = e.data.buf1;
for (var i = 0; i < workerScripts.length; i++) {
if (workerScripts[i].name == scriptName) {
//Stop the script from running and then remove it from workerScripts
clearTimeout(workerScripts[i].timeout);
workerScripts.splice(i, 1);
}
}
}
var ast = Parser(Tokenizer(InputStream(code)));
var globalEnv = new Environment();
evaluate(ast, globalEnv);
}
//Loop through workerScripts and run every script that is not currently running
function runScriptsLoop() {
console.log("runScriptsLoop() iteration");
for (var i = 0; i < workerScripts.length; i++) {
if (workerScripts[i].running == false) {
var ast = Parser(Tokenizer(InputStream(workerScripts[i].code)));

@ -440,6 +440,7 @@ var Terminal = {
s.code = Player.currentServer.scripts[i].code;
workerScripts.push(s);
console.log("Pushed script onto workerScripts");
return;
}
}
}

@ -147,7 +147,6 @@ var Engine = {
},
/* Main Event Loop */
_scriptUpdateStatusCounter: 0,
idleTimer: function() {
//Get time difference
var _thisUpdate = new Date().getTime();
@ -156,20 +155,12 @@ var Engine = {
//Divide this by cycle time to determine how many cycles have elapsed since last update
diff = Math.round(diff / Engine._idleSpeed);
Engine._scriptUpdateStatusCounter += diff;
if (diff > 0) {
//Update the game engine by the calculated number of cycles
Engine.updateGame(diff);
Engine._lastUpdate = _thisUpdate;
}
if (Engine._scriptUpdateStatusCounter >= 50) {
console.log("Updating Script Status");
Engine._scriptUpdateStatusCounter = 0;
//Engine.updateScriptStatus();
}
window.requestAnimationFrame(Engine.idleTimer);
},
@ -226,16 +217,6 @@ var Engine = {
}
},
/* NetScript Web Worker Stuff */
_scriptWebWorker: null,
updateScriptStatus: function() {
Engine._scriptWebWorker.postMessage(
{'type': "Status Update",
'buf1': JSON.stringify(ForeignServers),
'buf2': JSON.stringify(Player)}
)
},
/* Initialization */
init: function() {
//Initialization functions