mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 04:35:46 +01:00
Unify error handling
This commit is contained in:
parent
8dd507883a
commit
5798c4c7d3
@ -126,15 +126,15 @@ function installAugmentations(force?: boolean): boolean {
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = ` - ${ownedAug.level}`;
|
||||
}
|
||||
augmentationList += aug.name + level + "<br>";
|
||||
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:<br>" +
|
||||
"to install the following Augmentations:\n" +
|
||||
augmentationList +
|
||||
"<br>You wake up in your home...you feel different...",
|
||||
"\nYou wake up in your home...you feel different...",
|
||||
);
|
||||
}
|
||||
prestigeAugmentation();
|
||||
|
@ -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<T, U> extends React.Component<T, U> {
|
||||
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;
|
||||
}
|
||||
|
@ -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.<br><br>" +
|
||||
"This is a bug. Please report to game developer.\n\n" +
|
||||
"(Your funds have been set to $150b for the inconvenience)",
|
||||
);
|
||||
this.funds = 150e9;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 += `<br>${numeralWrapper.format(
|
||||
privateShares,
|
||||
"0.000a",
|
||||
)} of these shares were bought by private investors.`;
|
||||
}
|
||||
dialogContents += `<br><br>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);
|
||||
}
|
||||
|
||||
|
@ -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.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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!<br>" +
|
||||
"You now have access to the dark web from your home computer.<br>" +
|
||||
"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.",
|
||||
);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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: <br><br>" +
|
||||
"<i>" +
|
||||
msg.msg +
|
||||
"</i><br><br>" +
|
||||
"This message was saved as " +
|
||||
msg.filename +
|
||||
" onto your home computer.";
|
||||
dialogBoxCreate(txt);
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
Message received from unknown sender:<i>{msg.msg}</i>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, Message> = {
|
||||
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. <br><br>It's real, I've seen it. And I can " +
|
||||
"help you find it. But not right now. You're not ready yet.<br><br>" +
|
||||
"Use this program to track your progress<br><br>" +
|
||||
"The fl1ght.exe program was added to your home computer<br><br>" +
|
||||
"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. <br><br>" +
|
||||
"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.<br><br>" +
|
||||
"They are not what they seem. No one is.\n\n" +
|
||||
"-jump3R",
|
||||
),
|
||||
|
||||
@ -148,21 +146,21 @@ const Messages: Record<MessageFilenames, Message> = {
|
||||
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. <br><br>Watch out for a hacking group known as ${FactionNames.NiteSec}.` +
|
||||
"<br><br>-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}. <br><br>` +
|
||||
"I.I.I.I <br><br>-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.<br><br>" +
|
||||
"The bits are all around us. The runners will help you.\n\n" +
|
||||
"-jump3R",
|
||||
),
|
||||
|
||||
@ -171,8 +169,8 @@ const Messages: Record<MessageFilenames, Message> = {
|
||||
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. <br><br>" +
|
||||
"But first, you must pass our test. Find and install the backdoor on our server. <br><br>" +
|
||||
"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<MessageFilenames, Message> = {
|
||||
"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. <br><br>" +
|
||||
"Join us, and people will fear you, too. <br><br>" +
|
||||
"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." +
|
||||
`<br><br>-${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. <br><br> " +
|
||||
"We can help you find the answers.<br><br>" +
|
||||
"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, Message> = {
|
||||
[MessageFilenames.TruthGazer]: new Message(
|
||||
MessageFilenames.TruthGazer,
|
||||
//"THE TRUTH CAN NO LONGER ESCAPE YOUR GAZE"
|
||||
"@&*($#@&__TH3__#@A&#@*)__TRU1H__(*)&*)($#@&()E&R)W&<br>" +
|
||||
"%@*$^$()@&$)$*@__CAN__()(@^#)@&@)#__N0__(#@&#)@&@&(<br>" +
|
||||
"*(__LON6ER__^#)@)(()*#@)@__ESCAP3__)#(@(#@*@()@(#*$<br>" +
|
||||
"@&*($#@&__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%*)@#(*%<br>" +
|
||||
")@B(*#%)@)M#B*%V)____FIND___#$@)#%(B*)@#(*%B)<br>" +
|
||||
"@_#(%_@#M(BDSPOMB__THE-CAVE_#)$(*@#$)@#BNBEGB<br>" +
|
||||
"@)(#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$)",
|
||||
),
|
||||
};
|
@ -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,
|
||||
|
@ -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 += `<br><br>Stack:<br>${userstack.join("<br>")}`;
|
||||
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`,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<number, IPort> = 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<void> {
|
||||
return new Promise<void>((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<void> =>
|
||||
executeJSScript(Player, workerScript.getServer().scripts, workerScript);
|
||||
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
const code = workerScript.code;
|
||||
@ -98,18 +65,16 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
codeWithImports = importProcessingRes.code;
|
||||
codeLineOffset = importProcessingRes.lineOffset;
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + 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<void> {
|
||||
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}<br>`;
|
||||
msg += "<br>";
|
||||
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<void> {
|
||||
} 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<void> {
|
||||
try {
|
||||
interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + 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<br>${scriptName}@${hostname} (PID - ${workerScript.pid})<br>`;
|
||||
if (workerScript.args.length > 0) {
|
||||
msg += `Args: ${arrayToString(workerScript.args)}<br>`;
|
||||
}
|
||||
msg += "<br>";
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -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!<br>" +
|
||||
"New update!\n" +
|
||||
"Please report any bugs/issues through the GitHub repository " +
|
||||
"or the Bitburner subreddit (reddit.com/r/bitburner).<br><br>" +
|
||||
"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.<br>" +
|
||||
"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).<br><br>" +
|
||||
"or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
|
||||
CONSTANTS.LatestUpdate,
|
||||
resets,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AutocompleteData, NS } from "../ScriptEditor/NetscriptDefinitions";
|
||||
|
||||
export interface ScriptModule {
|
||||
main?: (ns: NS) => Promise<void>;
|
||||
main?: (ns: NS) => unknown;
|
||||
autocomplete?: (data: AutocompleteData, flags: string[]) => unknown;
|
||||
}
|
||||
|
@ -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<br>You forgot to await a promise<br>${scriptName}@${hostname}<br>`;
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
dialogBoxCreate(msg);
|
||||
} 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} (PID - ${e.reason.pid})<br>` +
|
||||
`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"),
|
||||
);
|
||||
}
|
||||
|
@ -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(<Typography component="span" sx={styles} dangerouslySetInnerHTML={{ __html: txt }} />);
|
||||
}
|
||||
export function dialogBoxCreate(txt: string | JSX.Element): void {
|
||||
AlertEvents.emit(typeof txt === "string" ? <Typography component="span">{txt}</Typography> : 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user