From 5798c4c7d311f8792e74b49a58963b1c173451bd Mon Sep 17 00:00:00 2001
From: Snarling <84951833+Snarling@users.noreply.github.com>
Date: Sun, 28 Aug 2022 05:33:38 -0400
Subject: [PATCH] Unify error handling
---
src/Augmentation/AugmentationHelpers.tsx | 6 +-
src/Casino/Game.tsx | 4 +-
src/Corporation/Corporation.tsx | 2 +-
src/Corporation/Industry.ts | 35 +---
.../ui/modals/BribeFactionModal.tsx | 4 +-
.../ui/modals/IssueNewSharesModal.tsx | 13 +-
src/Corporation/ui/modals/ResearchModal.tsx | 4 +-
src/Corporation/ui/modals/ThrowPartyModal.tsx | 3 +-
src/Faction/FactionHelpers.tsx | 9 +-
src/Locations/LocationsHelpers.tsx | 4 +-
src/Locations/ui/TravelAgencyRoot.tsx | 2 +-
.../{MessageHelpers.ts => MessageHelpers.tsx} | 64 ++++---
src/Netscript/APIWrapper.ts | 2 +-
src/Netscript/NetscriptHelpers.ts | 53 +++---
src/Netscript/killWorkerScript.ts | 9 +-
src/NetscriptJSEvaluator.ts | 9 +-
src/NetscriptWorker.ts | 170 ++++--------------
src/SaveObject.tsx | 23 +--
src/Script/ScriptModule.ts | 2 +-
src/UncaughtPromiseHandler.ts | 25 +--
src/ui/React/DialogBox.tsx | 30 +++-
21 files changed, 149 insertions(+), 324 deletions(-)
rename src/Message/{MessageHelpers.ts => MessageHelpers.tsx} (82%)
diff --git a/src/Augmentation/AugmentationHelpers.tsx b/src/Augmentation/AugmentationHelpers.tsx
index a8ffa8e83..c388356f6 100644
--- a/src/Augmentation/AugmentationHelpers.tsx
+++ b/src/Augmentation/AugmentationHelpers.tsx
@@ -126,15 +126,15 @@ function installAugmentations(force?: boolean): boolean {
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
level = ` - ${ownedAug.level}`;
}
- augmentationList += aug.name + level + "
";
+ augmentationList += aug.name + level + "\n";
}
Player.queuedAugmentations = [];
if (!force) {
dialogBoxCreate(
"You slowly drift to sleep as scientists put you under in order " +
- "to install the following Augmentations:
" +
+ "to install the following Augmentations:\n" +
augmentationList +
- "
You wake up in your home...you feel different...",
+ "\nYou wake up in your home...you feel different...",
);
}
prestigeAugmentation();
diff --git a/src/Casino/Game.tsx b/src/Casino/Game.tsx
index 1da783d28..b20eda813 100644
--- a/src/Casino/Game.tsx
+++ b/src/Casino/Game.tsx
@@ -11,7 +11,7 @@ export function win(p: IPlayer, n: number): void {
export function reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if (reached) {
- dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.>);
+ dialogBoxCreate("Alright cheater get out of here. You're not allowed here anymore.");
}
return reached;
}
@@ -24,7 +24,7 @@ export class Game extends React.Component {
reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if (reached) {
- dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.>);
+ dialogBoxCreate("Alright cheater get out of here. You're not allowed here anymore.");
}
return reached;
}
diff --git a/src/Corporation/Corporation.tsx b/src/Corporation/Corporation.tsx
index cf6ac9125..d9643eddf 100644
--- a/src/Corporation/Corporation.tsx
+++ b/src/Corporation/Corporation.tsx
@@ -120,7 +120,7 @@ export class Corporation {
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate(
"There was an error calculating your Corporations funds and they got reset to 0. " +
- "This is a bug. Please report to game developer.
" +
+ "This is a bug. Please report to game developer.\n\n" +
"(Your funds have been set to $150b for the inconvenience)",
);
this.funds = 150e9;
diff --git a/src/Corporation/Industry.ts b/src/Corporation/Industry.ts
index 8ec68f26f..affcf04e2 100644
--- a/src/Corporation/Industry.ts
+++ b/src/Corporation/Industry.ts
@@ -825,14 +825,7 @@ export class Industry implements IIndustry {
sellAmt = eval(tmp);
} catch (e) {
dialogBoxCreate(
- "Error evaluating your sell amount for material " +
- mat.name +
- " in " +
- this.name +
- "'s " +
- city +
- " office. The sell amount " +
- "is being set to zero",
+ `Error evaluating your sell amount for material ${mat.name} in ${this.name}'s ${city} office. The sell amount is being set to zero`,
);
sellAmt = 0;
}
@@ -879,27 +872,13 @@ export class Industry implements IIndustry {
amt = eval(amtStr);
} catch (e) {
dialogBoxCreate(
- "Calculating export for " +
- mat.name +
- " in " +
- this.name +
- "'s " +
- city +
- " division failed with " +
- "error: " +
- e,
+ `Calculating export for ${mat.name} in ${this.name}'s ${city} division failed with error: ${e}`,
);
continue;
}
if (isNaN(amt)) {
dialogBoxCreate(
- "Error calculating export amount for " +
- mat.name +
- " in " +
- this.name +
- "'s " +
- city +
- " division.",
+ `Error calculating export amount for ${mat.name} in ${this.name}'s ${city} division.`,
);
continue;
}
@@ -1172,13 +1151,7 @@ export class Industry implements IIndustry {
tmp = eval(tmp);
} catch (e) {
dialogBoxCreate(
- "Error evaluating your sell price expression for " +
- product.name +
- " in " +
- this.name +
- "'s " +
- city +
- " office. Sell price is being set to MAX",
+ `Error evaluating your sell price expression for ${product.name} in ${this.name}'s ${city} office. Sell price is being set to MAX`,
);
tmp = product.maxsll;
}
diff --git a/src/Corporation/ui/modals/BribeFactionModal.tsx b/src/Corporation/ui/modals/BribeFactionModal.tsx
index 87d20105d..341c56772 100644
--- a/src/Corporation/ui/modals/BribeFactionModal.tsx
+++ b/src/Corporation/ui/modals/BribeFactionModal.tsx
@@ -60,9 +60,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
const fac = Factions[selectedFaction];
if (disabled) return;
const rep = repGain(money);
- dialogBoxCreate(
- "You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
- );
+ dialogBoxCreate(`You gained ${numeralWrapper.formatReputation(rep)} reputation with ${fac.name} by bribing them.`);
fac.playerReputation += rep;
corp.funds = corp.funds - money;
props.onClose();
diff --git a/src/Corporation/ui/modals/IssueNewSharesModal.tsx b/src/Corporation/ui/modals/IssueNewSharesModal.tsx
index 85c7f8b97..944d58203 100644
--- a/src/Corporation/ui/modals/IssueNewSharesModal.tsx
+++ b/src/Corporation/ui/modals/IssueNewSharesModal.tsx
@@ -89,14 +89,11 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
let dialogContents =
`Issued ${numeralWrapper.format(newShares, "0.000a")} new shares` +
- ` and raised ${numeralWrapper.formatMoney(profit)}.`;
- if (privateShares > 0) {
- dialogContents += `
${numeralWrapper.format(
- privateShares,
- "0.000a",
- )} of these shares were bought by private investors.`;
- }
- dialogContents += `
Stock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`;
+ ` and raised ${numeralWrapper.formatMoney(profit)}.` +
+ (privateShares > 0)
+ ? "\n" + numeralWrapper.format(privateShares, "0.000a") + "of these shares were bought by private investors."
+ : "";
+ dialogContents += `\n\nStock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`;
dialogBoxCreate(dialogContents);
}
diff --git a/src/Corporation/ui/modals/ResearchModal.tsx b/src/Corporation/ui/modals/ResearchModal.tsx
index 40570aba5..d0c47d3aa 100644
--- a/src/Corporation/ui/modals/ResearchModal.tsx
+++ b/src/Corporation/ui/modals/ResearchModal.tsx
@@ -42,9 +42,7 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
}
dialogBoxCreate(
- `Researched ${n.text}. It may take a market cycle ` +
- `(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
- `the Research apply.`,
+ `Researched ${n.text}. It may take a market cycle (~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of the Research apply.`,
);
}
diff --git a/src/Corporation/ui/modals/ThrowPartyModal.tsx b/src/Corporation/ui/modals/ThrowPartyModal.tsx
index 8bbd9d3f7..4190e6b85 100644
--- a/src/Corporation/ui/modals/ThrowPartyModal.tsx
+++ b/src/Corporation/ui/modals/ThrowPartyModal.tsx
@@ -41,8 +41,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
if (mult > 0) {
dialogBoxCreate(
- "You threw a party for the office! The morale and happiness " +
- "of each employee increased by " +
+ "You threw a party for the office! The morale and happiness of each employee increased by " +
numeralWrapper.formatPercentage(mult - 1),
);
}
diff --git a/src/Faction/FactionHelpers.tsx b/src/Faction/FactionHelpers.tsx
index 882cba6b7..6e7d9509b 100644
--- a/src/Faction/FactionHelpers.tsx
+++ b/src/Faction/FactionHelpers.tsx
@@ -94,12 +94,9 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
return "You purchased " + aug.name;
} else if (!Settings.SuppressBuyAugmentationConfirmation) {
dialogBoxCreate(
- "You purchased " +
- aug.name +
- ". Its enhancements will not take " +
- "effect until they are installed. To install your augmentations, go to the " +
- "'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
- "augmentations will now be more expensive.",
+ `You purchased ${aug.name}. Its enhancements will not take effect until they are installed.` +
+ "To install your augmentations, go to the 'Augmentations' tab on the left-hand navigation menu." +
+ "Purchasing additional augmentations will now be more expensive.",
);
}
} else {
diff --git a/src/Locations/LocationsHelpers.tsx b/src/Locations/LocationsHelpers.tsx
index 30bdabe5c..86e449f2e 100644
--- a/src/Locations/LocationsHelpers.tsx
+++ b/src/Locations/LocationsHelpers.tsx
@@ -33,8 +33,8 @@ export function purchaseTorRouter(p: IPlayer): void {
p.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
darkweb.serversOnNetwork.push(p.getHomeComputer().hostname);
dialogBoxCreate(
- "You have purchased a TOR router!
" +
- "You now have access to the dark web from your home computer.
" +
+ "You have purchased a TOR router!\n" +
+ "You now have access to the dark web from your home computer.\n" +
"Use the scan/scan-analyze commands to search for the dark web connection.",
);
}
diff --git a/src/Locations/ui/TravelAgencyRoot.tsx b/src/Locations/ui/TravelAgencyRoot.tsx
index 9e3b58b06..6711b8857 100644
--- a/src/Locations/ui/TravelAgencyRoot.tsx
+++ b/src/Locations/ui/TravelAgencyRoot.tsx
@@ -35,7 +35,7 @@ function travel(p: IPlayer, router: IRouter, to: CityName): void {
p.loseMoney(cost, "other");
p.travel(to);
- dialogBoxCreate(<>You are now in {to}!>);
+ dialogBoxCreate(`You are now in ${to}!`);
router.toCity();
}
diff --git a/src/Message/MessageHelpers.ts b/src/Message/MessageHelpers.tsx
similarity index 82%
rename from src/Message/MessageHelpers.ts
rename to src/Message/MessageHelpers.tsx
index 26764b72c..dc6d87d95 100644
--- a/src/Message/MessageHelpers.ts
+++ b/src/Message/MessageHelpers.tsx
@@ -1,3 +1,4 @@
+import React from "react";
import { Message } from "./Message";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { Router } from "../ui/GameRoot";
@@ -22,15 +23,12 @@ function sendMessage(msg: Message, forced = false): void {
function showMessage(name: MessageFilenames): void {
const msg = Messages[name];
if (!(msg instanceof Message)) throw new Error("trying to display unexistent message");
- const txt =
- "Message received from unknown sender:
" +
- "" +
- msg.msg +
- "
" +
- "This message was saved as " +
- msg.filename +
- " onto your home computer.";
- dialogBoxCreate(txt);
+ dialogBoxCreate(
+ <>
+ Message received from unknown sender:{msg.msg}This message was saved as {msg.filename} onto your home
+ computer.
+ >,
+ );
}
//Adds a message to a server
@@ -127,20 +125,20 @@ const Messages: Record = {
MessageFilenames.Jumper0,
"I know you can sense it. I know you're searching for it. " +
"It's why you spend night after " +
- "night at your computer.
It's real, I've seen it. And I can " +
- "help you find it. But not right now. You're not ready yet.
" +
- "Use this program to track your progress
" +
- "The fl1ght.exe program was added to your home computer
" +
+ "night at your computer. \n\nIt's real, I've seen it. And I can " +
+ "help you find it. But not right now. You're not ready yet.\n\n" +
+ "Use this program to track your progress\n\n" +
+ "The fl1ght.exe program was added to your home computer\n\n" +
"-jump3R",
),
[MessageFilenames.Jumper1]: new Message(
MessageFilenames.Jumper1,
`Soon you will be contacted by a hacking group known as ${FactionNames.NiteSec}. ` +
- "They can help you with your search.
" +
+ "They can help you with your search. \n\n" +
"You should join them, garner their favor, and " +
"exploit them for their Augmentations. But do not trust them. " +
- "They are not what they seem. No one is.
" +
+ "They are not what they seem. No one is.\n\n" +
"-jump3R",
),
@@ -148,21 +146,21 @@ const Messages: Record = {
MessageFilenames.Jumper2,
"Do not try to save the world. There is no world to save. If " +
"you want to find the truth, worry only about yourself. Ethics and " +
- `morals will get you killed.
Watch out for a hacking group known as ${FactionNames.NiteSec}.` +
- "
-jump3R",
+ `morals will get you killed. \n\nWatch out for a hacking group known as ${FactionNames.NiteSec}.` +
+ "\n\n-jump3R",
),
[MessageFilenames.Jumper3]: new Message(
MessageFilenames.Jumper3,
"You must learn to walk before you can run. And you must " +
- `run before you can fly. Look for ${FactionNames.TheBlackHand}.
` +
- "I.I.I.I
-jump3R",
+ `run before you can fly. Look for ${FactionNames.TheBlackHand}. \n\n` +
+ "I.I.I.I \n\n-jump3R",
),
[MessageFilenames.Jumper4]: new Message(
MessageFilenames.Jumper4,
"To find what you are searching for, you must understand the bits. " +
- "The bits are all around us. The runners will help you.
" +
+ "The bits are all around us. The runners will help you.\n\n" +
"-jump3R",
),
@@ -171,8 +169,8 @@ const Messages: Record = {
MessageFilenames.CyberSecTest,
"We've been watching you. Your skills are very impressive. But you're wasting " +
"your talents. If you join us, you can put your skills to good use and change " +
- "the world for the better. If you join us, we can unlock your full potential.
" +
- "But first, you must pass our test. Find and install the backdoor on our server.
" +
+ "the world for the better. If you join us, we can unlock your full potential. \n\n" +
+ "But first, you must pass our test. Find and install the backdoor on our server. \n\n" +
`-${FactionNames.CyberSec}`,
),
@@ -181,17 +179,17 @@ const Messages: Record = {
"People say that the corrupted governments and corporations rule the world. " +
"Yes, maybe they do. But do you know who everyone really fears? People " +
"like us. Because they can't hide from us. Because they can't fight shadows " +
- "and ideas with bullets.
" +
- "Join us, and people will fear you, too.
" +
+ "and ideas with bullets. \n\n" +
+ "Join us, and people will fear you, too. \n\n" +
"Find and install the backdoor on our server, avmnite-02h. Then, we will contact you again." +
- `
-${FactionNames.NiteSec}`,
+ `\n\n-${FactionNames.NiteSec}`,
),
[MessageFilenames.BitRunnersTest]: new Message(
MessageFilenames.BitRunnersTest,
"We know what you are doing. We know what drives you. We know " +
- "what you are looking for.
" +
- "We can help you find the answers.
" +
+ "what you are looking for. \n\n " +
+ "We can help you find the answers.\n\n" +
"run4theh111z",
),
@@ -199,18 +197,18 @@ const Messages: Record = {
[MessageFilenames.TruthGazer]: new Message(
MessageFilenames.TruthGazer,
//"THE TRUTH CAN NO LONGER ESCAPE YOUR GAZE"
- "@&*($#@&__TH3__#@A@*)__TRU1H__(*)&*)($#@&()E&R)W&
" +
- "%@*$^$()@&$)$*@__CAN__()(@^#)@&@)#__N0__(#@)@&@&(
" +
- "*(__LON6ER__^#)@)(()*#@)@__ESCAP3__)#(@(#@*@()@(#*$
" +
+ "@&*($#@&__TH3__#@A@*)__TRU1H__(*)&*)($#@&()E&R)W&\n" +
+ "%@*$^$()@&$)$*@__CAN__()(@^#)@&@)#__N0__(#@)@&@&(\n" +
+ "*(__LON6ER__^#)@)(()*#@)@__ESCAP3__)#(@(#@*@()@(#*$\n" +
"()@)#$*%)$#()$#__Y0UR__(*)$#()%(&(%)*!)($__GAZ3__#(",
),
[MessageFilenames.RedPill]: new Message(
MessageFilenames.RedPill,
//"FIND THE-CAVE"
- "@)(#V%*N)@(#*)*C)@#%*)*V)@#(*%V@)(#VN%*)@#(*%
" +
- ")@B(*#%)@)M#B*%V)____FIND___#$@)#%(B*)@#(*%B)
" +
- "@_#(%_@#M(BDSPOMB__THE-CAVE_#)$(*@#$)@#BNBEGB
" +
+ "@)(#V%*N)@(#*)*C)@#%*)*V)@#(*%V@)(#VN%*)@#(*%\n" +
+ ")@B(*#%)@)M#B*%V)____FIND___#$@)#%(B*)@#(*%B)\n" +
+ "@_#(%_@#M(BDSPOMB__THE-CAVE_#)$(*@#$)@#BNBEGB\n" +
"DFLSMFVMV)#@($*)@#*$MV)@#(*$V)M#(*$)M@(#*VM$)",
),
};
diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts
index 3e1522e90..0bd5817d4 100644
--- a/src/Netscript/APIWrapper.ts
+++ b/src/Netscript/APIWrapper.ts
@@ -41,7 +41,7 @@ function wrapFunction(
const functionPath = tree.join(".");
const functionName = tree.pop();
if (typeof functionName !== "string") {
- throw helpers.makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api");
+ throw helpers.makeBasicErrorMsg(workerScript, "Failure occured while wrapping netscript api");
}
const ctx = {
workerScript,
diff --git a/src/Netscript/NetscriptHelpers.ts b/src/Netscript/NetscriptHelpers.ts
index 017463737..0ca89db76 100644
--- a/src/Netscript/NetscriptHelpers.ts
+++ b/src/Netscript/NetscriptHelpers.ts
@@ -3,7 +3,6 @@ import { WorkerScript } from "./WorkerScript";
import { GetAllServers, GetServer } from "../Server/AllServers";
import { Player } from "../Player";
import { ScriptDeath } from "./ScriptDeath";
-import { isString } from "../utils/helpers/isString";
import { numeralWrapper } from "../ui/numeralFormat";
import { ScriptArg } from "./ScriptArg";
import { CityName } from "../Locations/data/CityNames";
@@ -40,8 +39,7 @@ export const helpers = {
number,
scriptArgs,
argsToString,
- isScriptErrorMessage,
- makeRuntimeRejectMsg,
+ makeBasicErrorMsg,
makeRuntimeErrorMsg,
resolveNetscriptRequestedThreads,
checkEnvFlags,
@@ -114,15 +112,6 @@ function scriptArgs(ctx: NetscriptContext, args: unknown) {
return args;
}
-/** Determines if the given msg string is an error created by makeRuntimeRejectMsg. */
-function isScriptErrorMessage(msg: string): boolean {
- if (!isString(msg)) {
- return false;
- }
- const splitMsg = msg.split("|DELIMITER|");
- return splitMsg.length == 4;
-}
-
/** Convert multiple arguments for tprint or print into a single string. */
function argsToString(args: unknown[]): string {
let out = "";
@@ -143,12 +132,11 @@ function argsToString(args: unknown[]): string {
}
/** Creates an error message string containing hostname, scriptname, and the error message msg */
-function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string {
+function makeBasicErrorMsg(workerScript: WorkerScript, msg: string): string {
for (const scriptUrl of workerScript.scriptRef.dependencies) {
msg = msg.replace(new RegExp(scriptUrl.url, "g"), scriptUrl.filename);
}
-
- return "|DELIMITER|" + workerScript.hostname + "|DELIMITER|" + workerScript.name + "|DELIMITER|" + msg;
+ return msg;
}
/** Creates an error message string with a stack trace. */
@@ -156,9 +144,9 @@ function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string): string {
const errstack = new Error().stack;
if (errstack === undefined) throw new Error("how did we not throw an error?");
const stack = errstack.split("\n").slice(1);
- const workerScript = ctx.workerScript;
- const caller = ctx.function;
- const scripts = workerScript.getServer().scripts;
+ const ws = ctx.workerScript;
+ const caller = ctx.functionPath;
+ const scripts = ws.getServer().scripts;
const userstack = [];
for (const stackline of stack) {
let filename;
@@ -216,10 +204,10 @@ function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string): string {
userstack.push(`${filename}:L${call.line}@${call.func}`);
}
- workerScript.log(caller, () => msg);
+ log(ctx, () => msg);
let rejectMsg = `${caller}: ${msg}`;
if (userstack.length !== 0) rejectMsg += `
Stack:
${userstack.join("
")}`;
- return makeRuntimeRejectMsg(workerScript, rejectMsg);
+ return makeBasicErrorMsg(ws, rejectMsg);
}
/** Validate requested number of threads for h/g/w options */
@@ -230,14 +218,14 @@ function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThread
}
const requestedThreadsAsInt = requestedThreads | 0;
if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) {
- throw makeRuntimeRejectMsg(
- ctx.workerScript,
+ throw makeRuntimeErrorMsg(
+ ctx,
`Invalid thread count passed to ${ctx.function}: ${requestedThreads}. Threads must be a positive number.`,
);
}
if (requestedThreadsAsInt > threads) {
- throw makeRuntimeRejectMsg(
- ctx.workerScript,
+ throw makeRuntimeErrorMsg(
+ ctx,
`Too many threads requested by ${ctx.function}. Requested: ${requestedThreads}. Has: ${threads}.`,
);
}
@@ -258,17 +246,21 @@ function checkSingularityAccess(ctx: NetscriptContext): void {
/** Create an error if a script is dead or if concurrent ns function calls are made */
function checkEnvFlags(ctx: NetscriptContext): void {
const ws = ctx.workerScript;
- if (ws.env.stopFlag) throw new ScriptDeath(ws);
+ if (ws.env.stopFlag) {
+ log(ctx, () => "Failed to run due to script being killed.");
+ throw new ScriptDeath(ws);
+ }
if (ws.env.runningFn && ctx.function !== "asleep") {
- ws.errorMessage = makeRuntimeRejectMsg(
+ //This one has no error message so it will not create a dialog
+ if (ws.delayReject) ws.delayReject(new ScriptDeath(ws));
+ ws.errorMessage = makeBasicErrorMsg(
ws,
`Concurrent calls to Netscript functions are not allowed!
Did you forget to await hack(), grow(), or some other
promise-returning function?
Currently running: ${ws.env.runningFn} tried to run: ${ctx.function}`,
);
- if (ws.delayReject) ws.delayReject(new ScriptDeath(ws));
- throw new ScriptDeath(ws); //No idea if this is the right thing to throw
+ throw new ScriptDeath(ws);
}
}
@@ -302,7 +294,8 @@ function updateDynamicRam(ctx: NetscriptContext, ramCost: number): void {
}
ws.dynamicRamUsage += ramCost;
if (ws.dynamicRamUsage > 1.01 * ws.ramUsage) {
- ws.errorMessage = makeRuntimeRejectMsg(
+ log(ctx, () => "Insufficient static ram available.");
+ ws.errorMessage = makeBasicErrorMsg(
ws,
`Dynamic RAM usage calculated to be greater than initial RAM usage on fn: ${fnName}.
This is probably because you somehow circumvented the static RAM calculation.
@@ -605,7 +598,7 @@ function getRunningScriptByArgs(
scriptArgs: ScriptArg[],
): RunningScript | null {
if (!Array.isArray(scriptArgs)) {
- throw helpers.makeRuntimeRejectMsg(
+ throw helpers.makeBasicErrorMsg(
ctx.workerScript,
`Invalid scriptArgs argument passed into getRunningScript() from ${ctx.function}(). ` +
`This is probably a bug. Please report to game developer`,
diff --git a/src/Netscript/killWorkerScript.ts b/src/Netscript/killWorkerScript.ts
index b90ded508..f41e179c0 100644
--- a/src/Netscript/killWorkerScript.ts
+++ b/src/Netscript/killWorkerScript.ts
@@ -10,7 +10,7 @@ import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventE
import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers";
-import { dialogBoxCreate } from "../ui/React/DialogBox";
+import { errorDialog } from "../ui/React/DialogBox";
import { AddRecentScript } from "./RecentScripts";
import { Player } from "../Player";
import { ITutorial } from "../InteractiveTutorial";
@@ -66,12 +66,7 @@ function stopAndCleanUpWorkerScript(ws: WorkerScript): void {
ws.env.stopFlag = false;
ws.atExit();
} catch (e: unknown) {
- let message = e instanceof ScriptDeath ? e.errorMessage : String(e);
- message = message.replace(/.*\|DELIMITER\|/, "");
- dialogBoxCreate(
- `Error trying to call atExit for script ${[ws.name, ...ws.args].join(" ")} on ${ws.hostname}\n ${message}`,
- );
- console.error(e);
+ errorDialog(e, `Error during atExit ${ws.name}@${ws.hostname} (PID - ${ws.pid}\n\n`);
}
ws.atExit = undefined;
}
diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts
index 8e5f00861..9b384fe65 100644
--- a/src/NetscriptJSEvaluator.ts
+++ b/src/NetscriptJSEvaluator.ts
@@ -71,7 +71,7 @@ export async function executeJSScript(
const ns = workerScript.env.vars;
if (!loadedModule) {
- throw helpers.makeRuntimeRejectMsg(
+ throw helpers.makeBasicErrorMsg(
workerScript,
`${script.filename} cannot be run because the script module won't load`,
);
@@ -79,18 +79,19 @@ export async function executeJSScript(
// TODO: putting await in a non-async function yields unhelpful
// "SyntaxError: unexpected reserved word" with no line number information.
if (!loadedModule.main) {
- throw helpers.makeRuntimeRejectMsg(
+ throw helpers.makeBasicErrorMsg(
workerScript,
`${script.filename} cannot be run because it does not have a main function.`,
);
}
if (!ns) {
- throw helpers.makeRuntimeRejectMsg(
+ throw helpers.makeBasicErrorMsg(
workerScript,
`${script.filename} cannot be run because the NS object hasn't been constructed properly.`,
);
}
- return loadedModule.main(ns);
+ await loadedModule.main(ns);
+ return;
}
function isDependencyOutOfDate(filename: string, scripts: Script[], scriptModuleSequenceNumber: number): boolean {
diff --git a/src/NetscriptWorker.ts b/src/NetscriptWorker.ts
index 7f808adc4..1d05043aa 100644
--- a/src/NetscriptWorker.ts
+++ b/src/NetscriptWorker.ts
@@ -24,10 +24,9 @@ import { Settings } from "./Settings/Settings";
import { generate } from "escodegen";
-import { dialogBoxCreate } from "./ui/React/DialogBox";
+import { dialogBoxCreate, errorDialog } from "./ui/React/DialogBox";
import { arrayToString } from "./utils/helpers/arrayToString";
import { roundToTwo } from "./utils/helpers/roundToTwo";
-import { isString } from "./utils/helpers/isString";
import { parse } from "acorn";
import { simple as walksimple } from "acorn-walk";
@@ -35,7 +34,6 @@ import { areFilesEqual } from "./Terminal/DirectoryHelpers";
import { Player } from "./Player";
import { Terminal } from "./Terminal";
import { ScriptArg } from "./Netscript/ScriptArg";
-import { helpers } from "./Netscript/NetscriptHelpers";
export const NetscriptPorts: Map = new Map();
@@ -54,39 +52,8 @@ 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(workerScript: WorkerScript): Promise {
- return new Promise((resolve, reject) => {
- executeJSScript(Player, workerScript.getServer().scripts, workerScript)
- .then(() => {
- resolve();
- })
- .catch((e) => reject(e));
- }).catch((e) => {
- if (e instanceof Error) {
- if (e instanceof SyntaxError) {
- workerScript.errorMessage = helpers.makeRuntimeRejectMsg(
- workerScript,
- e.message + " (sorry we can't be more helpful)",
- );
- } else {
- workerScript.errorMessage = helpers.makeRuntimeRejectMsg(
- workerScript,
- e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
- );
- }
- throw new ScriptDeath(workerScript);
- } else if (helpers.isScriptErrorMessage(e)) {
- workerScript.errorMessage = e;
- throw new ScriptDeath(workerScript);
- } else if (e instanceof ScriptDeath) {
- throw e;
- }
-
- // Don't know what to do with it, let's try making an error message out of it
- workerScript.errorMessage = helpers.makeRuntimeRejectMsg(workerScript, "" + e);
- throw new ScriptDeath(workerScript);
- });
-}
+const startNetscript2Script = (workerScript: WorkerScript): Promise =>
+ executeJSScript(Player, workerScript.getServer().scripts, workerScript);
function startNetscript1Script(workerScript: WorkerScript): Promise {
const code = workerScript.code;
@@ -98,18 +65,16 @@ function startNetscript1Script(workerScript: WorkerScript): Promise {
codeWithImports = importProcessingRes.code;
codeLineOffset = importProcessingRes.lineOffset;
} catch (e: unknown) {
- dialogBoxCreate("Error processing Imports in " + workerScript.name + ":
" + String(e));
+ dialogBoxCreate(`Error processing Imports in ${workerScript.name} on ${workerScript.hostname}:\n\n${e}`);
workerScript.env.stopFlag = true;
killWorkerScript(workerScript);
return Promise.resolve();
}
- function wrapNS1Layer(int: Interpreter, intLayer: unknown, path: string[] = []) {
- //TODO: Better typing layers of interpreter scope and ns
- interface BasicObject {
- [key: string]: any;
- }
- const nsLayer = path.reduce((prev, newPath) => prev[newPath], workerScript.env.vars as BasicObject);
+ interface BasicObject {
+ [key: string]: any;
+ }
+ function wrapNS1Layer(int: Interpreter, intLayer: unknown, nsLayer = workerScript.env.vars as BasicObject) {
for (const [name, entry] of Object.entries(nsLayer)) {
if (typeof entry === "function") {
// Async functions need to be wrapped. See JS-Interpreter documentation
@@ -121,21 +86,11 @@ function startNetscript1Script(workerScript: WorkerScript): Promise {
const result = await entry(...args.map((arg) => int.pseudoToNative(arg)));
return callback(int.nativeToPseudo(result));
} catch (e: unknown) {
- // TODO: Unify error handling, this was stolen from previous async handler
- if (typeof e === "string") {
- console.error(e);
- const errorTextArray = e.split("|DELIMITER|");
- const hostname = errorTextArray[1];
- const scriptName = errorTextArray[2];
- const errorMsg = errorTextArray[3];
- let msg = `${scriptName}@${hostname}
`;
- msg += "
";
- msg += errorMsg;
- dialogBoxCreate(msg);
- workerScript.env.stopFlag = true;
- killWorkerScript(workerScript);
- return;
- }
+ // NS1 interpreter doesn't cleanly handle throwing. Need to show dialog here.
+ errorDialog(e, `RUNTIME ERROR:\n${workerScript.name}@${workerScript.hostname}\n\n`);
+ workerScript.env.stopFlag = true;
+ killWorkerScript(workerScript);
+ return;
}
};
int.setProperty(intLayer, name, int.createAsyncFunction(wrapper));
@@ -145,7 +100,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise {
} else {
// new object layer, e.g. bladeburner
int.setProperty(intLayer, name, int.nativeToPseudo({}));
- wrapNS1Layer(int, (intLayer as BasicObject).properties[name], [...path, name]);
+ wrapNS1Layer(int, (intLayer as BasicObject).properties[name], nsLayer[name]);
}
}
}
@@ -154,54 +109,30 @@ function startNetscript1Script(workerScript: WorkerScript): Promise {
try {
interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
} catch (e: unknown) {
- dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":
" + String(e));
+ dialogBoxCreate(`Syntax ERROR in ${workerScript.name} on ${workerScript.hostname}:\n\n${String(e)}`);
workerScript.env.stopFlag = true;
killWorkerScript(workerScript);
return Promise.resolve();
}
- return new Promise(function (resolve, reject) {
+ return new Promise((resolve) => {
function runInterpreter(): void {
- try {
- if (workerScript.env.stopFlag) {
- return reject(new ScriptDeath(workerScript));
- }
+ if (workerScript.env.stopFlag) resolve();
- let more = true;
- let i = 0;
- while (i < 3 && more) {
- more = more && interpreter.step();
- i++;
- }
-
- if (more) {
- setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
- } else {
- resolve();
- }
- } catch (_e: unknown) {
- let e = String(_e);
- if (!helpers.isScriptErrorMessage(e)) {
- e = helpers.makeRuntimeRejectMsg(workerScript, e);
- }
- workerScript.errorMessage = e;
- return reject(new ScriptDeath(workerScript));
+ let more = true;
+ let i = 0;
+ while (i < 3 && more) {
+ more = more && interpreter.step();
+ i++;
}
- }
- try {
- runInterpreter();
- } catch (e: unknown) {
- if (isString(e)) {
- workerScript.errorMessage = e;
- return reject(new ScriptDeath(workerScript));
- } else if (e instanceof ScriptDeath) {
- return reject(e);
+ if (more) {
+ setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
} else {
- console.error(e);
- return reject(new ScriptDeath(workerScript));
+ resolve();
}
}
+ runInterpreter();
});
}
@@ -430,58 +361,19 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
// running status to false
scriptExecution
.then(function () {
- workerScript.env.stopFlag = true;
// On natural death, the earnings are transfered to the parent if it still exists.
- if (parent !== undefined && !parent.env.stopFlag) {
+ if (parent && !parent.env.stopFlag) {
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
}
-
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: " + e.toString());
- return;
- } else if (e instanceof ScriptDeath) {
- if (helpers.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: " + workerScript.errorMessage);
- return;
- }
- const hostname = errorTextArray[1];
- const scriptName = errorTextArray[2];
- const errorMsg = errorTextArray[3];
-
- let msg = `RUNTIME ERROR
${scriptName}@${hostname} (PID - ${workerScript.pid})
`;
- if (workerScript.args.length > 0) {
- msg += `Args: ${arrayToString(workerScript.args)}
`;
- }
- msg += "
";
- msg += errorMsg;
-
- dialogBoxCreate(msg);
- workerScript.log("", () => "Script crashed with runtime error");
- } else {
- workerScript.log("", () => "Script killed");
- return; // Already killed, so stop here
- }
- } else if (helpers.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: " +
- e.toString(),
- );
- return;
- } else {
- dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
- console.error(e);
- }
-
+ errorDialog(e, `RUNTIME ERROR\n${workerScript.name}@${workerScript.hostname} (PID - ${workerScript.pid})\n\n`);
+ let logText = "Script crashed due to an error.";
+ if (e instanceof ScriptDeath) logText = "Script killed.";
+ workerScript.log("", () => logText);
killWorkerScript(workerScript);
});
diff --git a/src/SaveObject.tsx b/src/SaveObject.tsx
index b1cf7a872..451198ec1 100755
--- a/src/SaveObject.tsx
+++ b/src/SaveObject.tsx
@@ -27,7 +27,6 @@ import { AwardNFG, v1APIBreak } from "./utils/v1APIBreak";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
import { LocationName } from "./Locations/data/LocationNames";
-import { SxProps } from "@mui/system";
import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
import { pushGameSaved } from "./Electron";
import { defaultMonacoTheme } from "./ScriptEditor/ui/themes";
@@ -759,27 +758,14 @@ function createScamUpdateText(): void {
}
}
-const resets: SxProps = {
- "& h1, & h2, & h3, & h4, & p, & a, & ul": {
- margin: 0,
- color: Settings.theme.primary,
- whiteSpace: "initial",
- },
- "& ul": {
- paddingLeft: "1.5em",
- lineHeight: 1.5,
- },
-};
-
function createNewUpdateText(): void {
setTimeout(
() =>
dialogBoxCreate(
- "New update!
" +
+ "New update!\n" +
"Please report any bugs/issues through the GitHub repository " +
- "or the Bitburner subreddit (reddit.com/r/bitburner).
" +
+ "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
CONSTANTS.LatestUpdate,
- resets,
),
1000,
);
@@ -788,11 +774,10 @@ function createNewUpdateText(): void {
function createBetaUpdateText(): void {
dialogBoxCreate(
"You are playing on the beta environment! This branch of the game " +
- "features the latest developments in the game. This version may be unstable.
" +
+ "features the latest developments in the game. This version may be unstable.\n" +
"Please report any bugs/issues through the github repository (https://github.com/danielyxie/bitburner/issues) " +
- "or the Bitburner subreddit (reddit.com/r/bitburner).
" +
+ "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
CONSTANTS.LatestUpdate,
- resets,
);
}
diff --git a/src/Script/ScriptModule.ts b/src/Script/ScriptModule.ts
index f5a3ee425..96da00616 100644
--- a/src/Script/ScriptModule.ts
+++ b/src/Script/ScriptModule.ts
@@ -1,6 +1,6 @@
import { AutocompleteData, NS } from "../ScriptEditor/NetscriptDefinitions";
export interface ScriptModule {
- main?: (ns: NS) => Promise;
+ main?: (ns: NS) => unknown;
autocomplete?: (data: AutocompleteData, flags: string[]) => unknown;
}
diff --git a/src/UncaughtPromiseHandler.ts b/src/UncaughtPromiseHandler.ts
index e37404a33..a786c384a 100644
--- a/src/UncaughtPromiseHandler.ts
+++ b/src/UncaughtPromiseHandler.ts
@@ -1,24 +1,7 @@
-import { ScriptDeath } from "./Netscript/ScriptDeath";
-import { helpers } from "./Netscript/NetscriptHelpers";
-import { dialogBoxCreate } from "./ui/React/DialogBox";
+import { errorDialog } from "./ui/React/DialogBox";
export function setupUncaughtPromiseHandler(): void {
- window.addEventListener("unhandledrejection", function (e) {
- if (helpers.isScriptErrorMessage(e.reason)) {
- const errorTextArray = e.reason.split("|DELIMITER|");
- const hostname = errorTextArray[1];
- const scriptName = errorTextArray[2];
- const errorMsg = errorTextArray[3];
-
- let msg = `UNCAUGHT PROMISE ERROR
You forgot to await a promise
${scriptName}@${hostname}
`;
- msg += "
";
- msg += errorMsg;
- dialogBoxCreate(msg);
- } else if (e.reason instanceof ScriptDeath) {
- const msg =
- `UNCAUGHT PROMISE ERROR
You forgot to await a promise
${e.reason.name}@${e.reason.hostname} (PID - ${e.reason.pid})
` +
- `Maybe hack / grow / weaken ?`;
- dialogBoxCreate(msg);
- }
- });
+ window.addEventListener("unhandledrejection", (e) =>
+ errorDialog(e.reason, "UNCAUGHT PROMISE ERROR\nYou forgot to await a promise\nmaybe hack / grow / weaken ?\n"),
+ );
}
diff --git a/src/ui/React/DialogBox.tsx b/src/ui/React/DialogBox.tsx
index 9ae8035f6..f4fe5216d 100644
--- a/src/ui/React/DialogBox.tsx
+++ b/src/ui/React/DialogBox.tsx
@@ -1,13 +1,29 @@
import { AlertEvents } from "./AlertManager";
import React from "react";
-import { SxProps } from "@mui/system";
import { Typography } from "@mui/material";
+import { ScriptDeath } from "../../Netscript/ScriptDeath";
-export function dialogBoxCreate(txt: string | JSX.Element, styles?: SxProps): void {
- if (typeof txt !== "string") {
- AlertEvents.emit(txt);
- } else {
- AlertEvents.emit();
- }
+export function dialogBoxCreate(txt: string | JSX.Element): void {
+ AlertEvents.emit(typeof txt === "string" ? {txt} : txt);
+}
+
+export function errorDialog(e: unknown, initialText = "") {
+ let errorText = "";
+ if (typeof e === "string") errorText = e;
+ else if (e instanceof ScriptDeath) {
+ if (!e.errorMessage) return; //No need for a dialog for an empty ScriptDeath
+ errorText = e.errorMessage;
+ } else if (e instanceof SyntaxError) errorText = e.message + " (sorry we can't be more helpful)";
+ else if (e instanceof Error) errorText = e.message + (e.stack ? `\nstack:\n${e.stack.toString()}` : "");
+ else {
+ errorText = "An unknown error was thrown, see console.";
+ console.error(e);
+ }
+
+ if (!initialText) {
+ if (e instanceof ScriptDeath) initialText = `${e.name}@${e.hostname} (PID - ${e.pid})\n\n`;
+ }
+
+ dialogBoxCreate(initialText + errorText);
}