mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-22 17:23:00 +02:00
DARKNET: Darkweb Expansion Project & Bitnode (#2139)
This is BN15. It is a really big change; see the PR for all the details.
This commit is contained in:
committed by
GitHub
parent
a674633f6c
commit
6073964768
@@ -0,0 +1,740 @@
|
||||
import type { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||
import type { Darknet as DarknetAPI, DarknetResult } from "@nsdefs";
|
||||
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import {
|
||||
calculateAuthenticationTime,
|
||||
calculatePasswordAttemptChaGain,
|
||||
chargeServerMigration,
|
||||
getBackdoorAuthTimeDebuff,
|
||||
getSetStasisLinkDuration,
|
||||
getStasisLinkLimit,
|
||||
setStasisLink,
|
||||
} from "../DarkNet/effects/effects";
|
||||
import { Player } from "@player";
|
||||
import { formatNumber } from "../ui/formatNumber";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
import { capturePackets } from "../DarkNet/models/packetSniffing";
|
||||
import { addSessionToServer, DarknetState, getServerState } from "../DarkNet/models/DarknetState";
|
||||
import { getStockFromSymbol } from "./StockMarket";
|
||||
import { CompletedProgramName } from "@enums";
|
||||
import { handleStormSeed } from "../DarkNet/effects/webstorm";
|
||||
import { getPasswordType } from "../DarkNet/controllers/ServerGenerator";
|
||||
import { checkPassword, getAuthResult, isAuthenticated } from "../DarkNet/effects/authentication";
|
||||
import {
|
||||
getLabMaze,
|
||||
getLabyrinthDetails,
|
||||
getLabyrinthLocationReport,
|
||||
getSurroundingsVisualized,
|
||||
isLabyrinthServer,
|
||||
} from "../DarkNet/effects/labyrinth";
|
||||
import { getPhishingAttackSpeed, handlePhishingAttack } from "../DarkNet/effects/phishing";
|
||||
import { handleRamBlockRemoved } from "../DarkNet/effects/ramblock";
|
||||
import {
|
||||
expectDarknetAccess,
|
||||
expectRunningOnDarknetServer,
|
||||
getFailureResult,
|
||||
getTimeoutChance,
|
||||
isDirectConnected,
|
||||
logger,
|
||||
} from "../DarkNet/effects/offlineServerHandling";
|
||||
import { DarknetServer } from "../Server/DarknetServer";
|
||||
import { GenericResponseMessage, ResponseCodeEnum } from "../DarkNet/Enums";
|
||||
import { getRewardFromCache } from "../DarkNet/effects/cacheFiles";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { getStasisLinkServers } from "../DarkNet/utils/darknetNetworkUtils";
|
||||
import { resolveCacheFilePath } from "../Paths/CacheFilePath";
|
||||
import type { CacheResult } from "@nsdefs";
|
||||
import { MAX_PASSWORD_LENGTH } from "../DarkNet/Constants";
|
||||
import { isIPAddress } from "../Types/strings";
|
||||
import { getDarknetServerOrThrow } from "../DarkNet/utils/darknetServerUtils";
|
||||
import { shuffle } from "lodash";
|
||||
|
||||
type CompleteHeartbleedOptions = {
|
||||
peek: boolean;
|
||||
logsToCapture: number;
|
||||
additionalMsec: number;
|
||||
};
|
||||
|
||||
function heartbleedOptions(ctx: NetscriptContext, opts: unknown): CompleteHeartbleedOptions {
|
||||
const defaults = {
|
||||
peek: false,
|
||||
logsToCapture: 1,
|
||||
additionalMsec: 0,
|
||||
};
|
||||
if (opts == null) {
|
||||
return defaults;
|
||||
}
|
||||
if (typeof opts !== "object") {
|
||||
throw helpers.errorMessage(ctx, `Invalid arguments: "options" is not an object`);
|
||||
}
|
||||
const options = {
|
||||
...defaults,
|
||||
...opts,
|
||||
};
|
||||
const peek = helpers.boolean(ctx, "options.peek", options.peek);
|
||||
const logsToCapture = helpers.positiveInteger(ctx, "options.logsToCapture", options.logsToCapture);
|
||||
if (logsToCapture > 8) {
|
||||
throw helpers.errorMessage(
|
||||
ctx,
|
||||
`Invalid arguments: "options.logsToCapture" (${options.logsToCapture}) must be smaller than or equal to 8`,
|
||||
);
|
||||
}
|
||||
const additionalMsec = helpers.integer(ctx, "options.additionalMsec", options.additionalMsec);
|
||||
if (additionalMsec < 0) {
|
||||
throw helpers.errorMessage(
|
||||
ctx,
|
||||
`Invalid arguments: "options.additionalMsec" (${options.additionalMsec}) must be a non-negative integer`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
peek,
|
||||
logsToCapture,
|
||||
additionalMsec,
|
||||
};
|
||||
}
|
||||
|
||||
export function NetscriptDarknet(): InternalAPI<DarknetAPI> {
|
||||
return {
|
||||
authenticate:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_host, _password, _additionalMsec): Promise<DarknetResult> => {
|
||||
const targetHost = helpers.string(ctx, "host", _host);
|
||||
const password = helpers.string(ctx, "password", _password);
|
||||
const additionalMsec = helpers.number(ctx, "additionalMsec", _additionalMsec ?? 0);
|
||||
if (additionalMsec < 0) {
|
||||
throw helpers.errorMessage(ctx, `Invalid arguments: "additionalMsec" is not a positive integer`);
|
||||
}
|
||||
if (password.length > MAX_PASSWORD_LENGTH * 2) {
|
||||
// No password will ever be this long, and this prevents extremely long password attempts from causing performance issues,
|
||||
// or feedback loops where longer and longer passwords are attempted due to player script bugs.
|
||||
throw helpers.errorMessage(
|
||||
ctx,
|
||||
`Invalid arguments: "password" is too long. Attempted length: ${
|
||||
password.length
|
||||
}. Attempted password starts with ${password.slice(0, 100)} `,
|
||||
);
|
||||
}
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
|
||||
const threads = ctx.workerScript.scriptRef.threads;
|
||||
const networkDelay = calculateAuthenticationTime(server, Player, threads, password) + additionalMsec;
|
||||
|
||||
logger(ctx)(
|
||||
`Connecting to ${server.hostname} with password '${password}'... (Est: ${formatNumber(
|
||||
networkDelay / 1000,
|
||||
1,
|
||||
)}s)`,
|
||||
);
|
||||
|
||||
return helpers.netscriptDelay(ctx, networkDelay).then(() => {
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, { requireDirectConnection: true });
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
|
||||
const server = onlineConnectionCheck.server;
|
||||
// Authentication has a chance to timeout based on darknet instability
|
||||
if (Math.random() < getTimeoutChance()) {
|
||||
logger(ctx)(`Authentication to ${server.hostname} timed out due to network instability. Please try again.`);
|
||||
return {
|
||||
success: false,
|
||||
code: ResponseCodeEnum.RequestTimeOut,
|
||||
message: GenericResponseMessage.RequestTimeOut,
|
||||
};
|
||||
}
|
||||
|
||||
const authResult = getAuthResult(server, password, threads, networkDelay, ctx.workerScript.pid);
|
||||
const success = authResult.result.success;
|
||||
const xp = formatNumber(calculatePasswordAttemptChaGain(server, threads, success), 1);
|
||||
logger(ctx)(
|
||||
`Authentication on ${server.hostname} ${success ? "succeeded" : `failed. (Gained ${xp} cha xp)`}`,
|
||||
);
|
||||
|
||||
if (isLabyrinthServer(server.hostname)) {
|
||||
return {
|
||||
success: success,
|
||||
code: success ? ResponseCodeEnum.Success : ResponseCodeEnum.AuthFailure,
|
||||
message: authResult.response.message,
|
||||
data: authResult.response.data,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: success,
|
||||
code: success ? ResponseCodeEnum.Success : ResponseCodeEnum.AuthFailure,
|
||||
message: success ? GenericResponseMessage.Success : GenericResponseMessage.AuthFailure,
|
||||
};
|
||||
});
|
||||
},
|
||||
connectToSession:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_host, _password): DarknetResult => {
|
||||
const targetHost = helpers.string(ctx, "host", _host);
|
||||
const token = helpers.string(ctx, "password", _password);
|
||||
if (token.length > 100) {
|
||||
throw helpers.errorMessage(
|
||||
ctx,
|
||||
`Invalid arguments: "password" is too long. Attempted length: ${
|
||||
token.length
|
||||
}. Attempted password starts with ${token.slice(0, 100)} `,
|
||||
);
|
||||
}
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireAdminRights: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return {
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
};
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
|
||||
const result = checkPassword(server, token, ctx.workerScript.scriptRef.threads, ctx.workerScript.pid);
|
||||
if (result.code !== ResponseCodeEnum.Success) {
|
||||
logger(ctx)(
|
||||
`${server.hostname} does not recognise that password. Use ns.dnet.authenticate() to create a session.`,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
code: ResponseCodeEnum.AuthFailure,
|
||||
message: GenericResponseMessage.AuthFailure,
|
||||
};
|
||||
}
|
||||
addSessionToServer(server, ctx.workerScript.pid);
|
||||
logger(ctx)(`Authentication on ${server.hostname} succeeded.`);
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
};
|
||||
},
|
||||
heartbleed:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_host, _opts): Promise<DarknetResult & { logs: string[] }> => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
const options = heartbleedOptions(ctx, _opts);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
logs: [],
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
const networkDelay =
|
||||
calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads) * 1.5 +
|
||||
(options.additionalMsec ?? 0);
|
||||
logger(ctx)(
|
||||
`Attempting to extract data from ${server.hostname}... (Est: ${formatNumber(networkDelay / 1000, 1)}s)`,
|
||||
);
|
||||
|
||||
if (Player.skills.charisma < server.requiredCharismaSkill) {
|
||||
logger(ctx)(
|
||||
`You need a higher charisma level to extract data from ${server.hostname}. (${server.requiredHackingSkill} required)`,
|
||||
);
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: ResponseCodeEnum.NotEnoughCharisma,
|
||||
message: GenericResponseMessage.NotEnoughCharisma,
|
||||
logs: [],
|
||||
}));
|
||||
}
|
||||
|
||||
return helpers.netscriptDelay(ctx, networkDelay).then(() => {
|
||||
const xpGained = Player.mults.charisma_exp * 50 * ((500 + Player.skills.charisma) / 500);
|
||||
Player.gainCharismaExp(xpGained);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, { requireDirectConnection: true });
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return {
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
logs: [],
|
||||
};
|
||||
}
|
||||
const serverState = getServerState(server.hostname);
|
||||
|
||||
logger(ctx)(`Extracted log data from ${server.hostname}... (Gained ${formatNumber(xpGained, 1)} cha xp)`);
|
||||
|
||||
const capturedLogs = serverState.serverLogs.slice(0, options.logsToCapture);
|
||||
if (!options.peek) {
|
||||
serverState.serverLogs = serverState.serverLogs.slice(options.logsToCapture);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
logs: capturedLogs.map((log) =>
|
||||
typeof log.message === "string" ? log.message : JSON.stringify(log.message),
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
openCache:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_fileName, _suppressToast): CacheResult => {
|
||||
const fileName = helpers.string(ctx, "fileName", _fileName);
|
||||
const suppressToast = helpers.boolean(ctx, "suppressToast", _suppressToast ?? false);
|
||||
const server = expectRunningOnDarknetServer(ctx);
|
||||
expectDarknetAccess(ctx);
|
||||
|
||||
const path = resolveCacheFilePath(fileName);
|
||||
if (!path) {
|
||||
throw helpers.errorMessage(ctx, `Invalid cache file. (File must end in .cache) : ${fileName}`);
|
||||
}
|
||||
const hasCacheFile = server.caches.includes(path);
|
||||
if (!hasCacheFile) {
|
||||
throw helpers.errorMessage(ctx, `Cache file not found: ${fileName} on server ${server.hostname}`);
|
||||
}
|
||||
|
||||
server.caches = server.caches.filter((cache) => cache !== fileName);
|
||||
const result = getRewardFromCache(server, fileName, suppressToast);
|
||||
logger(ctx)(`Data file ${fileName} opened. ${result.message}.`);
|
||||
return result;
|
||||
},
|
||||
probe:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_returnByIp): string[] => {
|
||||
const returnByIP = helpers.boolean(ctx, "returnByIP", _returnByIp ?? false);
|
||||
const server = ctx.workerScript.getServer();
|
||||
const out = [];
|
||||
for (const neighbor of server.serversOnNetwork) {
|
||||
const neighborServer = GetServer(neighbor);
|
||||
if (!(neighborServer instanceof DarknetServer)) {
|
||||
continue;
|
||||
}
|
||||
const entry = helpers.returnServerID(neighborServer, { returnByIP });
|
||||
if (entry) {
|
||||
out.push(entry);
|
||||
}
|
||||
}
|
||||
helpers.log(ctx, () => `Returned ${out.length} connections for ${server.hostname}`);
|
||||
// The order of results is shuffled. This is to avoid clues to the network structure
|
||||
// like there are in the standard network's scan results order.
|
||||
return shuffle(out);
|
||||
},
|
||||
setStasisLink:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_shouldLink): Promise<DarknetResult> => {
|
||||
const shouldLink = helpers.boolean(ctx, "shouldLink", _shouldLink ?? true);
|
||||
const targetHost = ctx.workerScript.getServer().hostname;
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost);
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
const stasisLinkCount = getStasisLinkServers().length;
|
||||
const stasisLinkLimit = getStasisLinkLimit();
|
||||
if (shouldLink && stasisLinkCount >= stasisLinkLimit) {
|
||||
helpers.log(ctx, () => `Stasis link limit reached. (${stasisLinkCount}/${stasisLinkLimit})`);
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: ResponseCodeEnum.StasisLinkLimitReached,
|
||||
message: GenericResponseMessage.StasisLinkLimitReached,
|
||||
}));
|
||||
}
|
||||
helpers.log(
|
||||
ctx,
|
||||
() => `Beginning stasis ${shouldLink ? "" : "removal "}procedure on ${server.hostname}... (Est: 30s)`,
|
||||
);
|
||||
// setStasisLink's delay is hardcoded at 30s. We should skip this delay in Jest tests.
|
||||
return helpers
|
||||
.netscriptDelay(ctx, getSetStasisLinkDuration())
|
||||
.then(() => setStasisLink(ctx, server, shouldLink));
|
||||
},
|
||||
getStasisLinkLimit: (ctx: NetscriptContext) => (): number => {
|
||||
const limit = getStasisLinkLimit();
|
||||
logger(ctx)(`Stasis link limit: ${limit}`);
|
||||
return limit;
|
||||
},
|
||||
getStasisLinkedServers:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_returnByIP): string[] => {
|
||||
const returnByIp = helpers.boolean(ctx, "returnByIP", _returnByIP ?? false);
|
||||
const servers = getStasisLinkServers();
|
||||
const serverNames = servers.map((s) => (returnByIp ? s.ip : s.hostname));
|
||||
logger(ctx)(`Stasis linked servers: ${serverNames}`);
|
||||
return serverNames;
|
||||
},
|
||||
getServerAuthDetails: (ctx) => (_host) => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost);
|
||||
if (!onlineConnectionCheck.success) {
|
||||
logger(ctx)(onlineConnectionCheck.message);
|
||||
return {
|
||||
isOnline: false,
|
||||
isConnectedToCurrentServer: false,
|
||||
hasSession: false,
|
||||
modelId: "",
|
||||
passwordHint: "",
|
||||
data: "",
|
||||
logTrafficInterval: -1,
|
||||
passwordLength: -1,
|
||||
passwordFormat: "numeric",
|
||||
} satisfies ReturnType<DarknetAPI["getServerAuthDetails"]>;
|
||||
}
|
||||
const targetServer = onlineConnectionCheck.server;
|
||||
const localServer = ctx.workerScript.getServer();
|
||||
const isConnected = isDirectConnected(localServer, targetServer);
|
||||
const hasSession = isAuthenticated(targetServer, ctx.workerScript.pid);
|
||||
return {
|
||||
isOnline: true,
|
||||
isConnectedToCurrentServer: isConnected,
|
||||
hasSession,
|
||||
modelId: targetServer.modelId,
|
||||
passwordHint: targetServer.staticPasswordHint,
|
||||
data: targetServer.passwordHintData ?? "",
|
||||
logTrafficInterval: targetServer.logTrafficInterval,
|
||||
passwordLength: targetServer.password.length,
|
||||
passwordFormat: getPasswordType(targetServer.password),
|
||||
} satisfies ReturnType<DarknetAPI["getServerAuthDetails"]>;
|
||||
},
|
||||
packetCapture: (ctx) => (_host) => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
data: "",
|
||||
}));
|
||||
}
|
||||
|
||||
const server = onlineConnectionCheck.server;
|
||||
const networkDelay = calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads) * 4;
|
||||
const xp = formatNumber(calculatePasswordAttemptChaGain(server, ctx.workerScript.scriptRef.threads), 1);
|
||||
|
||||
logger(ctx)(`Captured some outgoing transmissions from ${server.hostname}. (Gained ${xp} cha xp)`);
|
||||
return helpers.netscriptDelay(ctx, networkDelay).then(() => {
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
data: capturePackets(server),
|
||||
};
|
||||
});
|
||||
},
|
||||
induceServerMigration:
|
||||
(ctx) =>
|
||||
(_host): Promise<DarknetResult> => {
|
||||
const targetHost = helpers.string(ctx, "host", _host);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
preventUseOnStationaryServers: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const hostOfCurrentServer = !isIPAddress(targetHost)
|
||||
? ctx.workerScript.hostname
|
||||
: getDarknetServerOrThrow(ctx.workerScript.hostname).ip;
|
||||
if (targetHost === hostOfCurrentServer) {
|
||||
const message = `Cannot induce migration on a script's own server. induceServerMigration must target a neighboring connected server.`;
|
||||
logger(ctx)(message);
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: ResponseCodeEnum.DirectConnectionRequired,
|
||||
message: message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
logger(ctx)(`Inducing server migration of ${server.hostname}... (Est: 6s)`);
|
||||
|
||||
// induceServerMigration's delay is hardcoded at 6s. We should skip this delay in Jest tests.
|
||||
return helpers.netscriptDelay(ctx, !CONSTANTS.isInTestEnvironment ? 6000 : 0).then(() => {
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
preventUseOnStationaryServers: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
const currentDepth = server.depth;
|
||||
const result = chargeServerMigration(server, ctx.workerScript.scriptRef.threads);
|
||||
|
||||
logger(ctx)(
|
||||
`Induced ${formatNumber(result.chargeIncrease * 100)}%. Migration prep is now at ${formatNumber(
|
||||
result.newCharge * 100,
|
||||
)}%. (Gained ${formatNumber(result.xpGained)} cha xp)`,
|
||||
);
|
||||
if (result.newCharge >= 1 && currentDepth < server.depth) {
|
||||
logger(ctx)(`${server.hostname} has been migrated!`);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
};
|
||||
});
|
||||
},
|
||||
unleashStormSeed: (ctx) => (): DarknetResult => {
|
||||
expectDarknetAccess(ctx);
|
||||
const server = ctx.workerScript.getServer();
|
||||
const hasStormSeed = server.programs.includes(CompletedProgramName.stormSeed);
|
||||
if (!hasStormSeed) {
|
||||
const result = `${CompletedProgramName.stormSeed} not found on ${server.hostname}`;
|
||||
logger(ctx)(result);
|
||||
return {
|
||||
success: false,
|
||||
code: ResponseCodeEnum.NotFound,
|
||||
message: GenericResponseMessage.NotFound,
|
||||
};
|
||||
}
|
||||
|
||||
const result = `The webstorm has been unleashed...`;
|
||||
logger(ctx)(result);
|
||||
handleStormSeed(server);
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
};
|
||||
},
|
||||
isDarknetServer: (ctx) => (_host) => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
const server = GetServer(targetHost);
|
||||
if (!server) {
|
||||
return false;
|
||||
}
|
||||
if (!(server instanceof DarknetServer)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
memoryReallocation:
|
||||
(ctx) =>
|
||||
(_host): Promise<DarknetResult> => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
requireAdminRights: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
|
||||
if (server.blockedRam <= 0) {
|
||||
logger(ctx)(`Server ${server.hostname} has no host-owned ram left to reallocate.`);
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: ResponseCodeEnum.NoBlockRAM,
|
||||
message: GenericResponseMessage.NoBlockRAM,
|
||||
}));
|
||||
}
|
||||
|
||||
logger(ctx)(`Attempting to liberate RAM from '${server.hostname}'s owner ...`);
|
||||
const delayTime = Math.max(8000 * (500 / (500 + Player.skills.charisma)), 200);
|
||||
|
||||
return helpers.netscriptDelay(ctx, delayTime).then(() => {
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost, {
|
||||
requireDirectConnection: true,
|
||||
requireAdminRights: true,
|
||||
});
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return helpers.netscriptDelay(ctx, 100).then(() => ({
|
||||
success: false,
|
||||
code: onlineConnectionCheck.code,
|
||||
message: onlineConnectionCheck.message,
|
||||
}));
|
||||
}
|
||||
const server = onlineConnectionCheck.server;
|
||||
if (server.blockedRam <= 0) {
|
||||
logger(ctx)(`Server ${server.hostname} has no host-owned ram left to reallocate.`);
|
||||
return {
|
||||
success: false,
|
||||
code: ResponseCodeEnum.NoBlockRAM,
|
||||
message: GenericResponseMessage.NoBlockRAM,
|
||||
};
|
||||
}
|
||||
return handleRamBlockRemoved(ctx, server);
|
||||
});
|
||||
},
|
||||
getBlockedRam:
|
||||
(ctx) =>
|
||||
(_host): number => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
expectRunningOnDarknetServer(ctx);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost);
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return 0;
|
||||
}
|
||||
return onlineConnectionCheck.server.blockedRam;
|
||||
},
|
||||
getDepth:
|
||||
(ctx) =>
|
||||
(_host): number => {
|
||||
const targetHost = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
|
||||
expectRunningOnDarknetServer(ctx);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost);
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return -1;
|
||||
}
|
||||
return onlineConnectionCheck.server.depth;
|
||||
},
|
||||
promoteStock:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol): Promise<DarknetResult> => {
|
||||
const symbol = helpers.string(ctx, "symbol", _symbol);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
expectRunningOnDarknetServer(ctx);
|
||||
expectDarknetAccess(ctx);
|
||||
|
||||
const waitTime = Math.max(8000 * (600 / (600 + Player.skills.charisma)), 200);
|
||||
logger(ctx)(
|
||||
`Spreading ${stock.name} stock propaganda to raise volatility... (Est: ${formatNumber(waitTime / 1000, 1)}s)`,
|
||||
);
|
||||
|
||||
return helpers.netscriptDelay(ctx, waitTime).then(() => {
|
||||
const threads = ctx.workerScript.scriptRef.threads;
|
||||
const promotionAmount = threads * ((500 + Player.skills.charisma) / 500);
|
||||
DarknetState.stockPromotions[symbol] = (DarknetState.stockPromotions[symbol] ?? 0) + promotionAmount;
|
||||
|
||||
const chaXp = Player.mults.charisma_exp * threads * 10 * ((200 + Player.skills.charisma) / 200);
|
||||
Player.gainCharismaExp(chaXp);
|
||||
|
||||
logger(ctx)(`Spread promotion for ${stock.name}. (Gained ${formatNumber(chaXp, 1)} cha xp)`);
|
||||
return {
|
||||
success: true,
|
||||
code: ResponseCodeEnum.Success,
|
||||
message: GenericResponseMessage.Success,
|
||||
};
|
||||
});
|
||||
},
|
||||
phishingAttack: (ctx: NetscriptContext) => (): Promise<DarknetResult> => {
|
||||
const waitTime = getPhishingAttackSpeed();
|
||||
const server = expectRunningOnDarknetServer(ctx);
|
||||
expectDarknetAccess(ctx);
|
||||
|
||||
return helpers.netscriptDelay(ctx, waitTime).then(() => {
|
||||
return handlePhishingAttack(ctx, server);
|
||||
});
|
||||
},
|
||||
getDarknetInstability: (ctx) => () => {
|
||||
expectDarknetAccess(ctx);
|
||||
return {
|
||||
authenticationDurationMultiplier: getBackdoorAuthTimeDebuff(),
|
||||
authenticationTimeoutChance: getTimeoutChance(),
|
||||
};
|
||||
},
|
||||
nextMutation: (ctx) => () => {
|
||||
expectDarknetAccess(ctx);
|
||||
return DarknetState.nextMutation;
|
||||
},
|
||||
getServerRequiredCharismaLevel:
|
||||
(ctx) =>
|
||||
(_host): number => {
|
||||
const targetHost = helpers.string(ctx, "host", _host);
|
||||
const onlineConnectionCheck = getFailureResult(ctx, targetHost);
|
||||
if (!onlineConnectionCheck.success) {
|
||||
return -1;
|
||||
}
|
||||
return onlineConnectionCheck.server.requiredCharismaSkill;
|
||||
},
|
||||
labreport: (ctx) => async () => {
|
||||
expectDarknetAccess(ctx);
|
||||
expectRunningOnDarknetServer(ctx);
|
||||
|
||||
const lab = getLabyrinthDetails().lab;
|
||||
if (!lab) {
|
||||
const status = "You feel lost...";
|
||||
logger(ctx)(status);
|
||||
return {
|
||||
success: false,
|
||||
message: status,
|
||||
};
|
||||
}
|
||||
|
||||
const currentServer = getDarknetServerOrThrow(ctx.workerScript.hostname);
|
||||
if (!isDirectConnected(currentServer, lab)) {
|
||||
const status = "You feel disconnected...";
|
||||
logger(ctx)(status);
|
||||
return {
|
||||
success: false,
|
||||
message: status,
|
||||
};
|
||||
}
|
||||
|
||||
const pid = ctx.workerScript.pid;
|
||||
const authenticationTime = calculateAuthenticationTime(lab, Player, ctx.workerScript.scriptRef.threads);
|
||||
await helpers.netscriptDelay(ctx, authenticationTime);
|
||||
|
||||
return getLabyrinthLocationReport(pid);
|
||||
},
|
||||
labradar: (ctx) => async () => {
|
||||
expectDarknetAccess(ctx);
|
||||
expectRunningOnDarknetServer(ctx);
|
||||
|
||||
const lab = getLabyrinthDetails().lab;
|
||||
if (!lab) {
|
||||
const status = "You feel blind...";
|
||||
logger(ctx)(status);
|
||||
return {
|
||||
success: false,
|
||||
message: status,
|
||||
};
|
||||
}
|
||||
|
||||
const currentServer = getDarknetServerOrThrow(ctx.workerScript.hostname);
|
||||
if (!isDirectConnected(currentServer, lab)) {
|
||||
const status = "You feel disconnected...";
|
||||
logger(ctx)(status);
|
||||
return {
|
||||
success: false,
|
||||
message: status,
|
||||
};
|
||||
}
|
||||
|
||||
const pid = ctx.workerScript.pid;
|
||||
const authenticationTime = calculateAuthenticationTime(lab, Player, ctx.workerScript.scriptRef.threads);
|
||||
await helpers.netscriptDelay(ctx, authenticationTime);
|
||||
|
||||
const [x, y] = DarknetState.labLocations[pid] ?? [1, 1];
|
||||
return {
|
||||
success: true,
|
||||
message: getSurroundingsVisualized(getLabMaze(), x, y, 3, true, true),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user