mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-20 06:33:49 +01:00
Merge pull request #2925 from Ornedan/necro-script-fix
Necro script fix
This commit is contained in:
commit
a17b81dff3
37
src/Netscript/ScriptDeath.ts
Normal file
37
src/Netscript/ScriptDeath.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
|
||||
/**
|
||||
* Script death marker.
|
||||
*
|
||||
* IMPORTANT: the game engine should not base any of it's decisions on the data
|
||||
* carried in a ScriptDeath instance.
|
||||
*
|
||||
* This is because ScriptDeath instances are thrown through player code when a
|
||||
* script is killed. Which grants the player access to the class and the ability
|
||||
* to construct new instances with arbitrary data.
|
||||
*/
|
||||
export class ScriptDeath {
|
||||
/** Process ID number. */
|
||||
pid: number;
|
||||
|
||||
/** Filename of the script. */
|
||||
name: string;
|
||||
|
||||
/** IP Address on which the script was running */
|
||||
hostname: string;
|
||||
|
||||
/** Status message in case of script error. */
|
||||
errorMessage = "";
|
||||
|
||||
constructor(ws: WorkerScript) {
|
||||
this.pid = ws.pid;
|
||||
this.name = ws.name;
|
||||
this.hostname = ws.hostname;
|
||||
this.errorMessage = ws.errorMessage;
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(ScriptDeath);
|
||||
Object.freeze(ScriptDeath.prototype);
|
@ -60,7 +60,7 @@ export class WorkerScript {
|
||||
env: Environment;
|
||||
|
||||
/**
|
||||
* Status message in case of script error. Currently unused I think
|
||||
* Status message in case of script error.
|
||||
*/
|
||||
errorMessage = "";
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Stops an actively-running script (represented by a WorkerScript object)
|
||||
* and removes it from the global pool of active scripts.
|
||||
*/
|
||||
import { ScriptDeath } from "./ScriptDeath";
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
import { workerScripts } from "./WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
||||
@ -139,7 +140,7 @@ function killNetscriptDelay(workerScript: WorkerScript): void {
|
||||
if (workerScript.delay) {
|
||||
clearTimeout(workerScript.delay);
|
||||
if (workerScript.delayReject) {
|
||||
workerScript.delayReject(workerScript);
|
||||
workerScript.delayReject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { isString } from "./utils/helpers/isString";
|
||||
import { GetServer } from "./Server/AllServers";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
|
||||
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
||||
// Cancel any pre-existing netscriptDelay'ed function call
|
||||
// TODO: the rejection almost certainly ends up in the uncaught rejection handler.
|
||||
// Maybe reject with a stack-trace'd error message?
|
||||
if (workerScript.delayReject) workerScript.delayReject();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
workerScript.delay = window.setTimeout(() => {
|
||||
workerScript.delay = null;
|
||||
workerScript.delayReject = undefined;
|
||||
|
||||
if (workerScript.env.stopFlag) reject(workerScript);
|
||||
if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript));
|
||||
else resolve();
|
||||
}, time);
|
||||
workerScript.delayReject = reject;
|
||||
|
@ -637,9 +637,6 @@ export function NetscriptCorporation(
|
||||
const office = getOffice(divisionName, cityName);
|
||||
if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`);
|
||||
return netscriptDelay(1000, workerScript).then(function () {
|
||||
if (workerScript.env.stopFlag) {
|
||||
return Promise.reject(workerScript);
|
||||
}
|
||||
return Promise.resolve(office.setEmployeeToJob(job, amount));
|
||||
});
|
||||
},
|
||||
|
@ -3,6 +3,7 @@
|
||||
* that allows for scripts to run
|
||||
*/
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||
@ -59,7 +60,7 @@ export function prestigeWorkerScripts(): void {
|
||||
// JS script promises need a little massaging to have the same guarantees as netscript
|
||||
// promises. This does said massaging and kicks the script off. It returns a promise
|
||||
// that resolves or rejects when the corresponding worker script is done.
|
||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<WorkerScript> {
|
||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> {
|
||||
workerScript.running = true;
|
||||
|
||||
// The name of the currently running netscript function, to prevent concurrent
|
||||
@ -79,7 +80,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
// This is not a problem for legacy Netscript because it also checks the
|
||||
// stop flag in the evaluator.
|
||||
if (workerScript.env.stopFlag) {
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
}
|
||||
|
||||
if (propName === "asleep") return f(...args); // OK for multiple simultaneous calls to sleep.
|
||||
@ -90,7 +91,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
"promise-returning function? (Currently running: %s tried to run: %s)";
|
||||
if (runningFn) {
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName));
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
}
|
||||
runningFn = propName;
|
||||
|
||||
@ -135,10 +136,10 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
|
||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||
// to that script, which env.vars does at this point.
|
||||
return new Promise<WorkerScript>((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
executeJSScript(player, workerScript.getServer().scripts, workerScript)
|
||||
.then(() => {
|
||||
resolve(workerScript);
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => reject(e));
|
||||
}).catch((e) => {
|
||||
@ -151,20 +152,21 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
|
||||
);
|
||||
}
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
} else if (isScriptErrorMessage(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
throw workerScript;
|
||||
} else if (e instanceof WorkerScript) {
|
||||
throw new ScriptDeath(workerScript);
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e);
|
||||
throw workerScript; // Don't know what to do with it, let's rethrow.
|
||||
// Don't know what to do with it, let's try making an error message out of it
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, "" + e);
|
||||
throw new ScriptDeath(workerScript);
|
||||
});
|
||||
}
|
||||
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript> {
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
const code = workerScript.code;
|
||||
workerScript.running = true;
|
||||
|
||||
@ -179,7 +181,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const interpreterInitialization = function (int: any, scope: any): void {
|
||||
@ -212,7 +214,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
})
|
||||
.catch(function (err: any) {
|
||||
// workerscript is when you cancel a delay
|
||||
if (!(err instanceof WorkerScript)) {
|
||||
if (!(err instanceof ScriptDeath)) {
|
||||
console.error(err);
|
||||
const errorTextArray = err.split("|DELIMITER|");
|
||||
const hostname = errorTextArray[1];
|
||||
@ -225,7 +227,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -288,14 +290,14 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
function runInterpreter(): void {
|
||||
try {
|
||||
if (workerScript.env.stopFlag) {
|
||||
return reject(workerScript);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
|
||||
let more = true;
|
||||
@ -308,7 +310,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
if (more) {
|
||||
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
||||
} else {
|
||||
resolve(workerScript);
|
||||
resolve();
|
||||
}
|
||||
} catch (e: any) {
|
||||
e = e.toString();
|
||||
@ -316,7 +318,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
e = makeRuntimeRejectMsg(workerScript, e);
|
||||
}
|
||||
workerScript.errorMessage = e;
|
||||
return reject(workerScript);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,11 +327,12 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
} catch (e: any) {
|
||||
if (isString(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
return reject(workerScript);
|
||||
} else if (e instanceof WorkerScript) {
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
return reject(e);
|
||||
} else {
|
||||
return reject(workerScript);
|
||||
console.error(e);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -552,29 +555,29 @@ function createAndAddWorkerScript(
|
||||
|
||||
// Create the WorkerScript. NOTE: WorkerScript ctor will set the underlying
|
||||
// RunningScript's PID as well
|
||||
const s = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||
s.ramUsage = oneRamUsage;
|
||||
const workerScript = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||
workerScript.ramUsage = oneRamUsage;
|
||||
|
||||
// Add the WorkerScript to the global pool
|
||||
workerScripts.set(pid, s);
|
||||
workerScripts.set(pid, workerScript);
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
|
||||
// Start the script's execution
|
||||
let p: Promise<WorkerScript> | null = null; // Script's resulting promise
|
||||
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
||||
p = startNetscript2Script(player, s);
|
||||
let scriptExecution: Promise<void> | null = null; // Script's resulting promise
|
||||
if (workerScript.name.endsWith(".js") || workerScript.name.endsWith(".ns")) {
|
||||
scriptExecution = startNetscript2Script(player, workerScript);
|
||||
} else {
|
||||
p = startNetscript1Script(s);
|
||||
if (!(p instanceof Promise)) {
|
||||
scriptExecution = startNetscript1Script(workerScript);
|
||||
if (!(scriptExecution instanceof Promise)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
p.then(function (w: WorkerScript) {
|
||||
w.running = false;
|
||||
w.env.stopFlag = true;
|
||||
scriptExecution.then(function () {
|
||||
workerScript.running = false;
|
||||
workerScript.env.stopFlag = true;
|
||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||
if (parent !== undefined) {
|
||||
if (parent.running) {
|
||||
@ -583,51 +586,51 @@ function createAndAddWorkerScript(
|
||||
}
|
||||
}
|
||||
|
||||
killWorkerScript(s);
|
||||
w.log("", () => "Script finished running");
|
||||
}).catch(function (w) {
|
||||
if (w instanceof Error) {
|
||||
killWorkerScript(workerScript);
|
||||
workerScript.log("", () => "Script finished running");
|
||||
}).catch(function (e) {
|
||||
if (e instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + e.toString());
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
const errorTextArray = w.errorMessage.split("|DELIMITER|");
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
if (isScriptErrorMessage(workerScript.errorMessage)) {
|
||||
const errorTextArray = workerScript.errorMessage.split("|DELIMITER|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.error("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.error("Error text: " + w.errorMessage);
|
||||
console.error("Error text: " + workerScript.errorMessage);
|
||||
return;
|
||||
}
|
||||
const hostname = errorTextArray[1];
|
||||
const scriptName = errorTextArray[2];
|
||||
const errorMsg = errorTextArray[3];
|
||||
|
||||
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname}<br>`;
|
||||
if (w.args.length > 0) {
|
||||
msg += `Args: ${arrayToString(w.args)}<br>`;
|
||||
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname} (PID - ${workerScript.pid})<br>`;
|
||||
if (workerScript.args.length > 0) {
|
||||
msg += `Args: ${arrayToString(workerScript.args)}<br>`;
|
||||
}
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
|
||||
dialogBoxCreate(msg);
|
||||
w.log("", () => "Script crashed with runtime error");
|
||||
workerScript.log("", () => "Script crashed with runtime error");
|
||||
} else {
|
||||
w.log("", () => "Script killed");
|
||||
workerScript.log("", () => "Script killed");
|
||||
return; // Already killed, so stop here
|
||||
}
|
||||
} else if (isScriptErrorMessage(w)) {
|
||||
} else if (isScriptErrorMessage(e)) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error(
|
||||
"ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " +
|
||||
w.toString(),
|
||||
e.toString(),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||
console.error(w);
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
killWorkerScript(s);
|
||||
killWorkerScript(workerScript);
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { isScriptErrorMessage } from "./NetscriptEvaluator";
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
|
||||
@ -14,9 +14,9 @@ export function setupUncaughtPromiseHandler(): void {
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
dialogBoxCreate(msg);
|
||||
} else if (e.reason instanceof WorkerScript) {
|
||||
} else if (e.reason instanceof ScriptDeath) {
|
||||
const msg =
|
||||
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname}<br>` +
|
||||
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname} (PID - ${e.reason.pid})<br>` +
|
||||
`Maybe hack / grow / weaken ?`;
|
||||
dialogBoxCreate(msg);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user