mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 20:25:45 +01:00
BUGFIX: Fix additionalMsec overflow issue (#941)
This commit is contained in:
parent
21c7f56d23
commit
61ffed9b3a
@ -7,7 +7,7 @@ import { Player } from "@player";
|
|||||||
import { ScriptDeath } from "./ScriptDeath";
|
import { ScriptDeath } from "./ScriptDeath";
|
||||||
import { formatExp, formatMoney, formatRam, formatThreads } from "../ui/formatNumber";
|
import { formatExp, formatMoney, formatRam, formatThreads } from "../ui/formatNumber";
|
||||||
import { ScriptArg } from "./ScriptArg";
|
import { ScriptArg } from "./ScriptArg";
|
||||||
import { BasicHGWOptions, RunningScript as IRunningScript, Person as IPerson, Server as IServer } from "@nsdefs";
|
import { RunningScript as IRunningScript, Person as IPerson, Server as IServer } from "@nsdefs";
|
||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
import {
|
import {
|
||||||
calculateHackingChance,
|
calculateHackingChance,
|
||||||
@ -49,7 +49,7 @@ export const helpers = {
|
|||||||
argsToString,
|
argsToString,
|
||||||
makeBasicErrorMsg,
|
makeBasicErrorMsg,
|
||||||
makeRuntimeErrorMsg,
|
makeRuntimeErrorMsg,
|
||||||
resolveNetscriptRequestedThreads,
|
validateHGWOptions,
|
||||||
checkEnvFlags,
|
checkEnvFlags,
|
||||||
checkSingularityAccess,
|
checkSingularityAccess,
|
||||||
netscriptDelay,
|
netscriptDelay,
|
||||||
@ -84,46 +84,18 @@ export interface CompleteRunOptions {
|
|||||||
export interface CompleteSpawnOptions extends CompleteRunOptions {
|
export interface CompleteSpawnOptions extends CompleteRunOptions {
|
||||||
spawnDelay: PositiveInteger;
|
spawnDelay: PositiveInteger;
|
||||||
}
|
}
|
||||||
|
/** HGWOptions with non-optional, type-validated members, for passing between internal functions. */
|
||||||
|
export interface CompleteHGWOptions {
|
||||||
|
threads: PositiveInteger;
|
||||||
|
stock: boolean;
|
||||||
|
additionalMsec: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function assertString(ctx: NetscriptContext, argName: string, v: unknown): asserts v is string {
|
export function assertString(ctx: NetscriptContext, argName: string, v: unknown): asserts v is string {
|
||||||
if (typeof v !== "string")
|
if (typeof v !== "string")
|
||||||
throw makeRuntimeErrorMsg(ctx, `${argName} expected to be a string. ${debugType(v)}`, "TYPE");
|
throw makeRuntimeErrorMsg(ctx, `${argName} expected to be a string. ${debugType(v)}`, "TYPE");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Will probably remove the below function in favor of a different approach to object type assertion.
|
|
||||||
* This method cannot be used to handle optional properties. */
|
|
||||||
export function assertObjectType<T extends object>(
|
|
||||||
ctx: NetscriptContext,
|
|
||||||
name: string,
|
|
||||||
obj: unknown,
|
|
||||||
desiredObject: T,
|
|
||||||
): asserts obj is T {
|
|
||||||
if (typeof obj !== "object" || obj === null) {
|
|
||||||
throw makeRuntimeErrorMsg(
|
|
||||||
ctx,
|
|
||||||
`Type ${obj === null ? "null" : typeof obj} provided for ${name}. Must be an object.`,
|
|
||||||
"TYPE",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (const [key, val] of Object.entries(desiredObject)) {
|
|
||||||
if (!Object.hasOwn(obj, key)) {
|
|
||||||
throw makeRuntimeErrorMsg(
|
|
||||||
ctx,
|
|
||||||
`Object provided for argument ${name} is missing required property ${key}.`,
|
|
||||||
"TYPE",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const objVal = (obj as Record<string, unknown>)[key];
|
|
||||||
if (typeof val !== typeof objVal) {
|
|
||||||
throw makeRuntimeErrorMsg(
|
|
||||||
ctx,
|
|
||||||
`Incorrect type ${typeof objVal} provided for property ${key} on ${name} argument. Should be type ${typeof val}.`,
|
|
||||||
"TYPE",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userFriendlyString = (v: unknown): string => {
|
const userFriendlyString = (v: unknown): string => {
|
||||||
const clip = (s: string): string => {
|
const clip = (s: string): string => {
|
||||||
if (s.length > 15) return s.slice(0, 12) + "...";
|
if (s.length > 15) return s.slice(0, 12) + "...";
|
||||||
@ -266,7 +238,7 @@ function makeBasicErrorMsg(ws: WorkerScript | ScriptDeath, msg: string, type = "
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an error message string with a stack trace. */
|
/** Creates an error message string with a stack trace. */
|
||||||
function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string, type = "RUNTIME"): string {
|
export function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string, type = "RUNTIME"): string {
|
||||||
const errstack = new Error().stack;
|
const errstack = new Error().stack;
|
||||||
if (errstack === undefined) throw new Error("how did we not throw an error?");
|
if (errstack === undefined) throw new Error("how did we not throw an error?");
|
||||||
const stack = errstack.split("\n").slice(1);
|
const stack = errstack.split("\n").slice(1);
|
||||||
@ -322,23 +294,44 @@ function makeRuntimeErrorMsg(ctx: NetscriptContext, msg: string, type = "RUNTIME
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Validate requested number of threads for h/g/w options */
|
function validateHGWOptions(ctx: NetscriptContext, opts: unknown): CompleteHGWOptions {
|
||||||
function resolveNetscriptRequestedThreads(ctx: NetscriptContext, requestedThreads?: number): number {
|
const result: CompleteHGWOptions = {
|
||||||
|
threads: 1 as PositiveInteger,
|
||||||
|
stock: false,
|
||||||
|
additionalMsec: 0,
|
||||||
|
};
|
||||||
|
if (opts == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (typeof opts !== "object") {
|
||||||
|
throw makeRuntimeErrorMsg(ctx, `BasicHGWOptions must be an object if specified, was ${opts}`);
|
||||||
|
}
|
||||||
|
// Safe assertion since threadOrOption type has been narrowed to a non-null object
|
||||||
|
const options = opts as Unknownify<CompleteHGWOptions>;
|
||||||
|
result.stock = !!options.stock;
|
||||||
|
result.additionalMsec = number(ctx, "opts.additionalMsec", options.additionalMsec ?? 0);
|
||||||
|
if (result.additionalMsec < 0) {
|
||||||
|
throw makeRuntimeErrorMsg(ctx, `additionalMsec must be non-negative, got ${options.additionalMsec}`);
|
||||||
|
}
|
||||||
|
if (result.additionalMsec > 1e9) {
|
||||||
|
throw makeRuntimeErrorMsg(ctx, `additionalMsec too large (>1e9), got ${options.additionalMsec}`);
|
||||||
|
}
|
||||||
|
const requestedThreads = options.threads;
|
||||||
const threads = ctx.workerScript.scriptRef.threads;
|
const threads = ctx.workerScript.scriptRef.threads;
|
||||||
if (!requestedThreads) {
|
if (!requestedThreads) {
|
||||||
return isNaN(threads) || threads < 1 ? 1 : threads;
|
result.threads = (isNaN(threads) || threads < 1 ? 1 : threads) as PositiveInteger;
|
||||||
|
} else {
|
||||||
|
const positiveThreads = positiveInteger(ctx, "opts.threads", requestedThreads);
|
||||||
|
if (positiveThreads > threads) {
|
||||||
|
throw makeRuntimeErrorMsg(
|
||||||
|
ctx,
|
||||||
|
`Too many threads requested by ${ctx.function}. Requested: ${positiveThreads}. Has: ${threads}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result.threads = positiveThreads;
|
||||||
}
|
}
|
||||||
const requestedThreadsAsInt = requestedThreads | 0;
|
|
||||||
if (isNaN(requestedThreads) || requestedThreadsAsInt < 1) {
|
return result;
|
||||||
throw makeRuntimeErrorMsg(ctx, `Invalid thread count: ${requestedThreads}. Threads must be a positive number.`);
|
|
||||||
}
|
|
||||||
if (requestedThreadsAsInt > threads) {
|
|
||||||
throw makeRuntimeErrorMsg(
|
|
||||||
ctx,
|
|
||||||
`Too many threads requested by ${ctx.function}. Requested: ${requestedThreads}. Has: ${threads}.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return requestedThreadsAsInt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Validate singularity access by throwing an error if the player does not have access. */
|
/** Validate singularity access by throwing an error if the player does not have access. */
|
||||||
@ -472,18 +465,9 @@ function isScriptArgs(args: unknown): args is ScriptArg[] {
|
|||||||
return Array.isArray(args) && args.every(isScriptArg);
|
return Array.isArray(args) && args.every(isScriptArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hack(
|
function hack(ctx: NetscriptContext, hostname: string, manual: boolean, opts: unknown): Promise<number> {
|
||||||
ctx: NetscriptContext,
|
|
||||||
hostname: string,
|
|
||||||
manual: boolean,
|
|
||||||
{ threads: requestedThreads, stock, additionalMsec: requestedSec }: BasicHGWOptions = {},
|
|
||||||
): Promise<number> {
|
|
||||||
const ws = ctx.workerScript;
|
const ws = ctx.workerScript;
|
||||||
const threads = helpers.resolveNetscriptRequestedThreads(ctx, requestedThreads);
|
const { threads, stock, additionalMsec } = validateHGWOptions(ctx, opts);
|
||||||
const additionalMsec = number(ctx, "opts.additionalMsec", requestedSec ?? 0);
|
|
||||||
if (additionalMsec < 0) {
|
|
||||||
throw makeRuntimeErrorMsg(ctx, `additionalMsec must be non-negative, got ${additionalMsec}`);
|
|
||||||
}
|
|
||||||
const server = getServer(ctx, hostname);
|
const server = getServer(ctx, hostname);
|
||||||
if (!(server instanceof Server)) {
|
if (!(server instanceof Server)) {
|
||||||
throw makeRuntimeErrorMsg(ctx, "Cannot be executed on this server.");
|
throw makeRuntimeErrorMsg(ctx, "Cannot be executed on this server.");
|
||||||
|
@ -51,7 +51,7 @@ import { runScriptFromScript } from "./NetscriptWorker";
|
|||||||
import { killWorkerScript, killWorkerScriptByPid } from "./Netscript/killWorkerScript";
|
import { killWorkerScript, killWorkerScriptByPid } from "./Netscript/killWorkerScript";
|
||||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||||
import { helpers, assertObjectType, wrapUserNode } from "./Netscript/NetscriptHelpers";
|
import { helpers, wrapUserNode } from "./Netscript/NetscriptHelpers";
|
||||||
import {
|
import {
|
||||||
formatExp,
|
formatExp,
|
||||||
formatNumberNoSuffix,
|
formatNumberNoSuffix,
|
||||||
@ -78,7 +78,7 @@ import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
|
|||||||
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
|
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
|
||||||
import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket";
|
import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket";
|
||||||
import { NetscriptGrafting } from "./NetscriptFunctions/Grafting";
|
import { NetscriptGrafting } from "./NetscriptFunctions/Grafting";
|
||||||
import { NS, RecentScript, BasicHGWOptions, ProcessInfo, NSEnums } from "@nsdefs";
|
import { NS, RecentScript, ProcessInfo, NSEnums } from "@nsdefs";
|
||||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||||
@ -154,15 +154,10 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
hasTorRouter: () => () => Player.hasTorRouter(),
|
hasTorRouter: () => () => Player.hasTorRouter(),
|
||||||
hack:
|
hack: (ctx) => (_hostname, opts?) => {
|
||||||
(ctx) =>
|
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||||
(_hostname, opts = {}) => {
|
return helpers.hack(ctx, hostname, false, opts);
|
||||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
},
|
||||||
// TODO 2.2: better type safety rework for functions using assertObjectType, then remove function.
|
|
||||||
const optsValidator: BasicHGWOptions = {};
|
|
||||||
assertObjectType(ctx, "opts", opts, optsValidator);
|
|
||||||
return helpers.hack(ctx, hostname, false, opts);
|
|
||||||
},
|
|
||||||
hackAnalyzeThreads: (ctx) => (_hostname, _hackAmount) => {
|
hackAnalyzeThreads: (ctx) => (_hostname, _hackAmount) => {
|
||||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||||
const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount);
|
const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount);
|
||||||
@ -252,66 +247,58 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
helpers.log(ctx, () => `Sleeping for ${convertTimeMsToTimeElapsedString(time, true)}.`);
|
helpers.log(ctx, () => `Sleeping for ${convertTimeMsToTimeElapsedString(time, true)}.`);
|
||||||
return new Promise((resolve) => setTimeout(() => resolve(true), time));
|
return new Promise((resolve) => setTimeout(() => resolve(true), time));
|
||||||
},
|
},
|
||||||
grow:
|
grow: (ctx) => (_hostname, opts?) => {
|
||||||
(ctx) =>
|
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||||
(_hostname, opts = {}) => {
|
const { threads, stock, additionalMsec } = helpers.validateHGWOptions(ctx, opts);
|
||||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
|
||||||
const optsValidator: BasicHGWOptions = {};
|
|
||||||
assertObjectType(ctx, "opts", opts, optsValidator);
|
|
||||||
const threads = helpers.resolveNetscriptRequestedThreads(ctx, opts.threads);
|
|
||||||
const additionalMsec = helpers.number(ctx, "opts.additionalMsec", opts.additionalMsec ?? 0);
|
|
||||||
if (additionalMsec < 0) {
|
|
||||||
throw helpers.makeRuntimeErrorMsg(ctx, `additionalMsec must be non-negative, got ${additionalMsec}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = helpers.getServer(ctx, hostname);
|
const server = helpers.getServer(ctx, hostname);
|
||||||
if (!(server instanceof Server)) {
|
if (!(server instanceof Server)) {
|
||||||
helpers.log(ctx, () => "Cannot be executed on this server.");
|
helpers.log(ctx, () => "Cannot be executed on this server.");
|
||||||
return Promise.resolve(0);
|
return Promise.resolve(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = GetServer(ctx.workerScript.hostname);
|
const host = GetServer(ctx.workerScript.hostname);
|
||||||
if (host === null) {
|
if (host === null) {
|
||||||
throw new Error("Workerscript host is null");
|
throw new Error("Workerscript host is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
// No root access or skill level too low
|
// No root access or skill level too low
|
||||||
const canHack = netscriptCanGrow(server);
|
const canHack = netscriptCanGrow(server);
|
||||||
if (!canHack.res) {
|
if (!canHack.res) {
|
||||||
throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || "");
|
throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
const growTime = calculateGrowTime(server, Player) + additionalMsec / 1000.0;
|
const growTime = calculateGrowTime(server, Player) + additionalMsec / 1000.0;
|
||||||
|
helpers.log(
|
||||||
|
ctx,
|
||||||
|
() =>
|
||||||
|
`Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(
|
||||||
|
growTime * 1000,
|
||||||
|
true,
|
||||||
|
)} (t=${formatThreads(threads)}).`,
|
||||||
|
);
|
||||||
|
return helpers.netscriptDelay(ctx, growTime * 1000).then(function () {
|
||||||
|
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
|
||||||
|
processSingleServerGrowth(server, threads, host.cpuCores);
|
||||||
|
const moneyAfter = server.moneyAvailable;
|
||||||
|
ctx.workerScript.scriptRef.recordGrow(server.hostname, threads);
|
||||||
|
const expGain = calculateHackingExpGain(server, Player) * threads;
|
||||||
|
const logGrowPercent = moneyAfter / moneyBefore - 1;
|
||||||
helpers.log(
|
helpers.log(
|
||||||
ctx,
|
ctx,
|
||||||
() =>
|
() =>
|
||||||
`Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(
|
`Available money on '${server.hostname}' grown by ${formatPercent(logGrowPercent, 6)}. Gained ${formatExp(
|
||||||
growTime * 1000,
|
expGain,
|
||||||
true,
|
)} hacking exp (t=${formatThreads(threads)}).`,
|
||||||
)} (t=${formatThreads(threads)}).`,
|
|
||||||
);
|
);
|
||||||
return helpers.netscriptDelay(ctx, growTime * 1000).then(function () {
|
ctx.workerScript.scriptRef.onlineExpGained += expGain;
|
||||||
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
|
Player.gainHackingExp(expGain);
|
||||||
processSingleServerGrowth(server, threads, host.cpuCores);
|
if (stock) {
|
||||||
const moneyAfter = server.moneyAvailable;
|
influenceStockThroughServerGrow(server, moneyAfter - moneyBefore);
|
||||||
ctx.workerScript.scriptRef.recordGrow(server.hostname, threads);
|
}
|
||||||
const expGain = calculateHackingExpGain(server, Player) * threads;
|
return Promise.resolve(moneyAfter / moneyBefore);
|
||||||
const logGrowPercent = moneyAfter / moneyBefore - 1;
|
});
|
||||||
helpers.log(
|
},
|
||||||
ctx,
|
|
||||||
() =>
|
|
||||||
`Available money on '${server.hostname}' grown by ${formatPercent(logGrowPercent, 6)}. Gained ${formatExp(
|
|
||||||
expGain,
|
|
||||||
)} hacking exp (t=${formatThreads(threads)}).`,
|
|
||||||
);
|
|
||||||
ctx.workerScript.scriptRef.onlineExpGained += expGain;
|
|
||||||
Player.gainHackingExp(expGain);
|
|
||||||
if (opts.stock) {
|
|
||||||
influenceStockThroughServerGrow(server, moneyAfter - moneyBefore);
|
|
||||||
}
|
|
||||||
return Promise.resolve(moneyAfter / moneyBefore);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
growthAnalyze:
|
growthAnalyze:
|
||||||
(ctx) =>
|
(ctx) =>
|
||||||
(_host, _multiplier, _cores = 1) => {
|
(_host, _multiplier, _cores = 1) => {
|
||||||
@ -359,64 +346,56 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
|
|
||||||
return 2 * CONSTANTS.ServerFortifyAmount * threads;
|
return 2 * CONSTANTS.ServerFortifyAmount * threads;
|
||||||
},
|
},
|
||||||
weaken:
|
weaken: (ctx) => async (_hostname, opts?) => {
|
||||||
(ctx) =>
|
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||||
async (_hostname, opts = {}) => {
|
const { threads, additionalMsec } = helpers.validateHGWOptions(ctx, opts);
|
||||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
|
||||||
const optsValidator: BasicHGWOptions = {};
|
|
||||||
assertObjectType(ctx, "opts", opts, optsValidator);
|
|
||||||
const threads = helpers.resolveNetscriptRequestedThreads(ctx, opts.threads);
|
|
||||||
const additionalMsec = helpers.number(ctx, "opts.additionalMsec", opts.additionalMsec ?? 0);
|
|
||||||
if (additionalMsec < 0) {
|
|
||||||
throw helpers.makeRuntimeErrorMsg(ctx, `additionalMsec must be non-negative, got ${additionalMsec}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = helpers.getServer(ctx, hostname);
|
const server = helpers.getServer(ctx, hostname);
|
||||||
if (!(server instanceof Server)) {
|
if (!(server instanceof Server)) {
|
||||||
helpers.log(ctx, () => "Cannot be executed on this server.");
|
helpers.log(ctx, () => "Cannot be executed on this server.");
|
||||||
|
return Promise.resolve(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No root access or skill level too low
|
||||||
|
const canHack = netscriptCanWeaken(server);
|
||||||
|
if (!canHack.res) {
|
||||||
|
throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const weakenTime = calculateWeakenTime(server, Player) + additionalMsec / 1000.0;
|
||||||
|
helpers.log(
|
||||||
|
ctx,
|
||||||
|
() =>
|
||||||
|
`Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(
|
||||||
|
weakenTime * 1000,
|
||||||
|
true,
|
||||||
|
)} (t=${formatThreads(threads)})`,
|
||||||
|
);
|
||||||
|
return helpers.netscriptDelay(ctx, weakenTime * 1000).then(function () {
|
||||||
|
const host = GetServer(ctx.workerScript.hostname);
|
||||||
|
if (host === null) {
|
||||||
|
helpers.log(ctx, () => "Server is null, did it die?");
|
||||||
return Promise.resolve(0);
|
return Promise.resolve(0);
|
||||||
}
|
}
|
||||||
|
const cores = host.cpuCores;
|
||||||
// No root access or skill level too low
|
const coreBonus = getCoreBonus(cores);
|
||||||
const canHack = netscriptCanWeaken(server);
|
const weakenAmt = CONSTANTS.ServerWeakenAmount * threads * coreBonus;
|
||||||
if (!canHack.res) {
|
server.weaken(weakenAmt);
|
||||||
throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || "");
|
ctx.workerScript.scriptRef.recordWeaken(server.hostname, threads);
|
||||||
}
|
const expGain = calculateHackingExpGain(server, Player) * threads;
|
||||||
|
|
||||||
const weakenTime = calculateWeakenTime(server, Player) + additionalMsec / 1000.0;
|
|
||||||
helpers.log(
|
helpers.log(
|
||||||
ctx,
|
ctx,
|
||||||
() =>
|
() =>
|
||||||
`Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(
|
`'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${formatExp(
|
||||||
weakenTime * 1000,
|
expGain,
|
||||||
true,
|
)} hacking exp (t=${formatThreads(threads)})`,
|
||||||
)} (t=${formatThreads(threads)})`,
|
|
||||||
);
|
);
|
||||||
return helpers.netscriptDelay(ctx, weakenTime * 1000).then(function () {
|
ctx.workerScript.scriptRef.onlineExpGained += expGain;
|
||||||
const host = GetServer(ctx.workerScript.hostname);
|
Player.gainHackingExp(expGain);
|
||||||
if (host === null) {
|
// Account for hidden multiplier in Server.weaken()
|
||||||
helpers.log(ctx, () => "Server is null, did it die?");
|
return Promise.resolve(weakenAmt * currentNodeMults.ServerWeakenRate);
|
||||||
return Promise.resolve(0);
|
});
|
||||||
}
|
},
|
||||||
const cores = helpers.getServer(ctx, ctx.workerScript.hostname).cpuCores;
|
|
||||||
const coreBonus = getCoreBonus(cores);
|
|
||||||
const weakenAmt = CONSTANTS.ServerWeakenAmount * threads * coreBonus;
|
|
||||||
server.weaken(weakenAmt);
|
|
||||||
ctx.workerScript.scriptRef.recordWeaken(server.hostname, threads);
|
|
||||||
const expGain = calculateHackingExpGain(server, Player) * threads;
|
|
||||||
helpers.log(
|
|
||||||
ctx,
|
|
||||||
() =>
|
|
||||||
`'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${formatExp(
|
|
||||||
expGain,
|
|
||||||
)} hacking exp (t=${formatThreads(threads)})`,
|
|
||||||
);
|
|
||||||
ctx.workerScript.scriptRef.onlineExpGained += expGain;
|
|
||||||
Player.gainHackingExp(expGain);
|
|
||||||
// Account for hidden multiplier in Server.weaken()
|
|
||||||
return Promise.resolve(weakenAmt * currentNodeMults.ServerWeakenRate);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
weakenAnalyze:
|
weakenAnalyze:
|
||||||
(ctx) =>
|
(ctx) =>
|
||||||
(_threads, _cores = 1) => {
|
(_threads, _cores = 1) => {
|
||||||
|
@ -530,7 +530,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
|||||||
manualHack: (ctx) => () => {
|
manualHack: (ctx) => () => {
|
||||||
helpers.checkSingularityAccess(ctx);
|
helpers.checkSingularityAccess(ctx);
|
||||||
const server = Player.getCurrentServer();
|
const server = Player.getCurrentServer();
|
||||||
return helpers.hack(ctx, server.hostname, true);
|
return helpers.hack(ctx, server.hostname, true, null);
|
||||||
},
|
},
|
||||||
installBackdoor: (ctx) => async (): Promise<void> => {
|
installBackdoor: (ctx) => async (): Promise<void> => {
|
||||||
helpers.checkSingularityAccess(ctx);
|
helpers.checkSingularityAccess(ctx);
|
||||||
|
@ -5,9 +5,43 @@ import { defaultTheme } from "../Themes/Themes";
|
|||||||
import { defaultStyles } from "../Themes/Styles";
|
import { defaultStyles } from "../Themes/Styles";
|
||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
import { hash } from "../hash/hash";
|
import { hash } from "../hash/hash";
|
||||||
import { InternalAPI } from "../Netscript/APIWrapper";
|
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||||
import { Terminal } from "../../src/Terminal";
|
import { Terminal } from "../../src/Terminal";
|
||||||
import { helpers, assertObjectType } from "../Netscript/NetscriptHelpers";
|
import { helpers, makeRuntimeErrorMsg } from "../Netscript/NetscriptHelpers";
|
||||||
|
|
||||||
|
/** Will probably remove the below function in favor of a different approach to object type assertion.
|
||||||
|
* This method cannot be used to handle optional properties. */
|
||||||
|
export function assertObjectType<T extends object>(
|
||||||
|
ctx: NetscriptContext,
|
||||||
|
name: string,
|
||||||
|
obj: unknown,
|
||||||
|
desiredObject: T,
|
||||||
|
): asserts obj is T {
|
||||||
|
if (typeof obj !== "object" || obj === null) {
|
||||||
|
throw makeRuntimeErrorMsg(
|
||||||
|
ctx,
|
||||||
|
`Type ${obj === null ? "null" : typeof obj} provided for ${name}. Must be an object.`,
|
||||||
|
"TYPE",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const [key, val] of Object.entries(desiredObject)) {
|
||||||
|
if (!Object.hasOwn(obj, key)) {
|
||||||
|
throw makeRuntimeErrorMsg(
|
||||||
|
ctx,
|
||||||
|
`Object provided for argument ${name} is missing required property ${key}.`,
|
||||||
|
"TYPE",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const objVal = (obj as Record<string, unknown>)[key];
|
||||||
|
if (typeof val !== typeof objVal) {
|
||||||
|
throw makeRuntimeErrorMsg(
|
||||||
|
ctx,
|
||||||
|
`Incorrect type ${typeof objVal} provided for property ${key} on ${name} argument. Should be type ${typeof val}.`,
|
||||||
|
"TYPE",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
|
export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
|
||||||
return {
|
return {
|
||||||
|
Loading…
Reference in New Issue
Block a user