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:
Michael Ficocelli
2026-02-03 06:40:36 -05:00
committed by GitHub
parent a674633f6c
commit 6073964768
225 changed files with 15010 additions and 526 deletions
+36 -17
View File
@@ -56,7 +56,7 @@ import { hasScriptExtension, ScriptFilePath } from "../Paths/ScriptFilePath";
import { CustomBoundary } from "../ui/Components/CustomBoundary";
import { ServerConstants } from "../Server/data/Constants";
import { errorMessage, log } from "./ErrorMessages";
import { assertStringWithNSContext, debugType, userFriendlyString } from "./TypeAssertion";
import { assertStringWithNSContext, debugType, missingKey, userFriendlyString } from "./TypeAssertion";
import {
canAccessBitNodeFeature,
getDefaultBitNodeOptions,
@@ -64,6 +64,9 @@ import {
} from "../BitNode/BitNodeUtils";
import { JSONMap } from "../Types/Jsonable";
import { Settings } from "../Settings/Settings";
import { Programs } from "../Programs/Programs";
import { getRecordKeys } from "../Types/Record";
import { DarknetServer } from "../Server/DarknetServer";
import { getFriendlyType } from "../utils/TypeAssertion";
export const helpers = {
@@ -494,16 +497,16 @@ function scriptIdentifier(
}
/**
* Gets the server with a specific hostname/ip. Throw an error if the server does not exist or it is an isolated
* Gets the server with a specific hostname/ip. Throw an error if the server does not exist or is an isolated non-dnet
* server (e.g., pre-TOR darkweb, pre-TRP WD).
*
* @param {NetscriptContext} ctx - Context from which getServer is being called. For logging purposes.
* @param {string} hostname - Hostname of the server
* @returns {BaseServer} The specified server as a BaseServer
*/
function getServer(ctx: NetscriptContext, hostname: string): BaseServer {
export function getServer(ctx: NetscriptContext, hostname: string): BaseServer {
const server = GetServer(hostname);
if (server == null || server.serversOnNetwork.length === 0) {
if (server == null || (server.serversOnNetwork.length == 0 && !(server instanceof DarknetServer))) {
const str = hostname === "" ? "'' (empty string)" : "'" + hostname + "'";
throw errorMessage(ctx, `Invalid hostname: ${str}`);
}
@@ -519,6 +522,8 @@ function getNormalServer(ctx: NetscriptContext, host: string): Server {
let errorMessage = `Cannot be executed on ${host}.`;
if (server instanceof HacknetServer) {
errorMessage += " The server must not be a hacknet server.";
} else if (server instanceof DarknetServer) {
errorMessage += " The server must not be a darknet server.";
}
throw helpers.errorMessage(ctx, errorMessage);
}
@@ -651,6 +656,9 @@ function person(ctx: NetscriptContext, p: unknown): IPerson {
return p as IPerson;
}
/**
* This function is used by non-dnet formulas APIs to check if the server data contains properties of a normal server.
*/
function server(ctx: NetscriptContext, s: unknown): IServer {
const fakeServer = {
hostname: undefined,
@@ -669,20 +677,22 @@ function server(ctx: NetscriptContext, s: unknown): IServer {
purchasedByPlayer: undefined,
};
const error = missingKey(fakeServer, s);
if (error) throw errorMessage(ctx, `server should be a Server.\n${error}`, "TYPE");
if (error) {
let errorMessagePrefix = "Server must be a normal server.";
if (s != null && typeof s === "object") {
if ("hostname" in s) {
errorMessagePrefix += ` Server's hostname is ${s.hostname}.`;
}
if ("modelId" in s) {
errorMessagePrefix += " Server data looks like darknet server data.";
}
}
// throw errorMessage(ctx, `Server should be a normal server.\n${error}`, "TYPE");
throw errorMessage(ctx, `${errorMessagePrefix}\n${error}`, "TYPE");
}
return s as IServer;
}
function missingKey(expect: object, actual: unknown): string | false {
if (typeof actual !== "object" || actual === null) {
return `Expected to be an object, was ${actual === null ? "null" : typeof actual}.`;
}
for (const key in expect) {
if (!(key in actual)) return `Property ${key} was expected but not present.`;
}
return false;
}
function gang(ctx: NetscriptContext, g: unknown): FormulaGang {
const error = missingKey({ respect: 0, territory: 0, wantedLevel: 0 }, g);
if (error) throw errorMessage(ctx, `gang should be a Gang.\n${error}`, "TYPE");
@@ -708,10 +718,19 @@ export function filePath(ctx: NetscriptContext, argName: string, filename: unkno
throw errorMessage(ctx, `Invalid ${argName}, was not a valid path: ${filename}`);
}
export function scriptPath(ctx: NetscriptContext, argName: string, filename: unknown): ScriptFilePath {
export function scriptPath(
ctx: NetscriptContext,
argName: string,
filename: unknown,
showExeErrorHint = false,
): ScriptFilePath {
const path = filePath(ctx, argName, filename);
if (hasScriptExtension(path)) return path;
throw errorMessage(ctx, `Invalid ${argName}, must be a script: ${filename}`);
const programName = getRecordKeys(Programs).find((name) => name.toLowerCase() === path.toLowerCase());
const nsMethod = programName ? Programs[programName].nsMethod : "";
const hint = nsMethod && showExeErrorHint ? `Did you mean to use ns.${nsMethod} ?` : "";
throw errorMessage(ctx, `Invalid ${argName}, must be a script (js, jsx, ts, tsx): ${filename} ${hint}`);
}
/**
+34
View File
@@ -234,6 +234,34 @@ const cloud = {
deleteServer: 2.25,
} as const;
// Darknet API
const dnet = {
authenticate: 0.4,
connectToSession: 0.05,
heartbleed: 0.6,
openCache: 2,
probe: RamCostConstants.Scan,
setStasisLink: 12,
getStasisLinkLimit: 0,
getStasisLinkedServers: 0,
getServer: 2,
getServerAuthDetails: RamCostConstants.GetServer,
packetCapture: 6,
induceServerMigration: 4,
unleashStormSeed: 0.1,
isDarknetServer: RamCostConstants.GetServer,
memoryReallocation: 1,
getBlockedRam: 0,
getDepth: RamCostConstants.GetServer,
promoteStock: 2,
phishingAttack: 2,
getDarknetInstability: 0,
nextMutation: RamCostConstants.CycleTiming,
getServerRequiredCharismaLevel: RamCostConstants.GetServer,
labreport: 0,
labradar: 0,
} as const;
const format = {
number: 0,
ram: 0,
@@ -515,6 +543,7 @@ export const RamCosts: RamCostTree<NSFull> = {
cloud,
gang,
go,
dnet,
bladeburner,
infiltration,
codingcontract,
@@ -699,6 +728,11 @@ export const RamCosts: RamCostTree<NSFull> = {
bladeburner: {
skillMaxUpgradeCount: 0,
},
dnet: {
getAuthenticateTime: 0,
getHeartbleedTime: 0,
getExpectedRamBlockRemoved: 0,
},
},
} as const;
+19
View File
@@ -1,3 +1,5 @@
import { exampleDarknetServerData } from "../DarkNet/Enums";
import type { DarknetServerData } from "@nsdefs";
import type { NetscriptContext } from "./APIWrapper";
import { errorMessage } from "./ErrorMessages";
@@ -55,3 +57,20 @@ export function assertFunctionWithNSContext(
): asserts v is () => void {
if (typeof v !== "function") throw errorMessage(ctx, `${argName} expected to be a function ${debugType(v)}`, "TYPE");
}
export function missingKey(expect: object, actual: unknown): string | false {
if (typeof actual !== "object" || actual === null) {
return `Expected to be an object, was ${actual === null ? "null" : typeof actual}.`;
}
for (const key in expect) {
if (!(key in actual)) return `Property ${key} was expected but not present.`;
}
return false;
}
export function assertDarknetServerData(ctx: NetscriptContext, data: unknown): asserts data is DarknetServerData {
const error = missingKey(exampleDarknetServerData, data);
if (error) {
throw errorMessage(ctx, `Invalid darknet server data.\n${error}`, "TYPE");
}
}
+21 -5
View File
@@ -12,6 +12,7 @@ import { ITutorial } from "../InteractiveTutorial";
import { AlertEvents } from "../ui/React/AlertManager";
import { handleUnknownError } from "../utils/ErrorHandler";
import { roundToTwo } from "../utils/helpers/roundToTwo";
import { BaseServer } from "../Server/BaseServer";
export function killWorkerScript(ws: WorkerScript): boolean {
if (ITutorial.isRunning) {
@@ -35,15 +36,30 @@ export function killWorkerScriptByPid(pid: number, killer?: WorkerScript): boole
}
export const killAllScripts = () => {
for (const server of GetAllServers()) {
for (const byPid of server.runningScriptMap.values()) {
for (const pid of byPid.keys()) {
killWorkerScriptByPid(pid);
}
for (const server of GetAllServers(true)) {
killServerScripts(server, "Script killed.");
}
};
export const killServerScripts = (server: BaseServer, message: string) => {
const scripts = server.runningScriptMap.values();
for (const byPid of scripts) {
for (const runningScript of byPid.values()) {
killWorkerScriptWithMessage(runningScript.pid, message);
}
}
};
function killWorkerScriptWithMessage(pid: number, message: string): boolean {
const ws = workerScripts.get(pid);
if (ws) {
ws.log("", () => message);
stopAndCleanUpWorkerScript(ws);
return true;
}
return false;
}
function stopAndCleanUpWorkerScript(ws: WorkerScript): void {
// Only clean up once.
// Important: Only this function can set stopFlag!