BUG: Fix missed cases in offline server handling (#2495)

There were two large holes in the existing offline server handling:

1. It didn't include IPs, so scripts that used IPs instead of hostnames
   would get exceptions thrown for "server not found."
2. Coverage was very low for non-Darknet APIs. Maybe most of them don't
   need to be covered, but many obvious ones like "ps", "killall" and
   "hasRootAccess" were missing. IMO the only reliable answer is one
   that enforces *all* are covered via the type system.

To accomplish the second part, helpers.getServer() was changed to return
null when a server is offline. This intentionally breaks a lot of its
utility, which was to return a server unconditionally. To compensate,
its utility was increased - it now also does unknown argument
processing, allowing it to subsume a common line that all callers were
repeating.

Some callers switched to ctx.workerScript.getServer(), because they
didn't actually need to be using helpers.getServer(). Similarly, a few
callsites switched to GetServerOrThrow(), for the cases where it should
be guaranteed that the server is valid. The rest are returning a
default/failure response when the server is offline. (Except for
contracts, which threw on failure already anyway.)
This commit is contained in:
David Walker
2026-02-15 10:29:47 -08:00
committed by GitHub
parent b5ab495837
commit b51ed8fd59
50 changed files with 478 additions and 471 deletions
@@ -76,5 +76,5 @@ RAM cost: 2 GB
Generate a dummy contract on the current server with no reward. Used to test various algorithms. Generate a dummy contract on the current server with no reward. Used to test various algorithms.
This function will return null and not generate a contract if the randomized contract name is the same as another contract's name. This function will return null and not generate a contract if the randomized contract name is the same as another contract's name or the host is offline.
+2 -2
View File
@@ -9,7 +9,7 @@ Runs BruteSSH.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
brutessh(host: string): boolean; brutessh(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Given a hostname, returns its IP address; or given an IP address, returns its ho
**Signature:** **Signature:**
```typescript ```typescript
dnsLookup(host: string): string; dnsLookup(host?: string): string;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+1 -1
View File
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
Hostname/IP of the `target server` on which to execute the script. Hostname/IP of the target server on which to execute the script.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Runs FTPCrack.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
ftpcrack(host: string): boolean; ftpcrack(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the execution time of a grow() call.
**Signature:** **Signature:**
```typescript ```typescript
getGrowTime(host: string): number; getGrowTime(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the execution time of a hack() call.
**Signature:** **Signature:**
```typescript ```typescript
getHackTime(host: string): number; getHackTime(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the exp gain of a script.
**Signature:** **Signature:**
```typescript ```typescript
getScriptExpGain(script: string, host: string, ...args: ScriptArg[]): number; getScriptExpGain(script: string, host?: string, ...args: ScriptArg[]): number;
``` ```
## Parameters ## Parameters
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
Hostname/IP of the server on which script is running. _(Optional)_ Hostname/IP of the server on which the script is running. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the income of a script.
**Signature:** **Signature:**
```typescript ```typescript
getScriptIncome(script: string, host: string, ...args: ScriptArg[]): number; getScriptIncome(script: string, host?: string, ...args: ScriptArg[]): number;
``` ```
## Parameters ## Parameters
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
Hostname/IP of the server on which script is running. _(Optional)_ Hostname/IP of the server on which the script is running. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Get the base security level of a server.
**Signature:** **Signature:**
```typescript ```typescript
getServerBaseSecurityLevel(host: string): number; getServerBaseSecurityLevel(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get a server growth parameter.
**Signature:** **Signature:**
```typescript ```typescript
getServerGrowth(host: string): number; getServerGrowth(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the maximum money available on a server.
**Signature:** **Signature:**
```typescript ```typescript
getServerMaxMoney(host: string): number; getServerMaxMoney(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the maximum amount of RAM on a server.
**Signature:** **Signature:**
```typescript ```typescript
getServerMaxRam(host: string): number; getServerMaxRam(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Returns the minimum security level of the target server.
**Signature:** **Signature:**
```typescript ```typescript
getServerMinSecurityLevel(host: string): number; getServerMinSecurityLevel(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Get money available on a server.
**Signature:** **Signature:**
```typescript ```typescript
getServerMoneyAvailable(host: string): number; getServerMoneyAvailable(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Returns the number of open ports required to successfully run NUKE.exe on the sp
**Signature:** **Signature:**
```typescript ```typescript
getServerNumPortsRequired(host: string): number; getServerNumPortsRequired(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Returns the required hacking level of the target server.
**Signature:** **Signature:**
```typescript ```typescript
getServerRequiredHackingLevel(host: string): number; getServerRequiredHackingLevel(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
@@ -9,7 +9,7 @@ Get server security level.
**Signature:** **Signature:**
```typescript ```typescript
getServerSecurityLevel(host: string): number; getServerSecurityLevel(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the used RAM on a server.
**Signature:** **Signature:**
```typescript ```typescript
getServerUsedRam(host: string): number; getServerUsedRam(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the execution time of a weaken() call.
**Signature:** **Signature:**
```typescript ```typescript
getWeakenTime(host: string): number; getWeakenTime(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Spoof money in a server's bank account, increasing the amount available.
**Signature:** **Signature:**
```typescript ```typescript
grow(host: string, opts?: BasicHGWOptions): Promise<number>; grow(host?: string, opts?: BasicHGWOptions): Promise<number>;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server to grow. _(Optional)_ Hostname/IP of the target server to grow. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Steal a server's money.
**Signature:** **Signature:**
```typescript ```typescript
hack(host: string, opts?: BasicHGWOptions): Promise<number>; hack(host?: string, opts?: BasicHGWOptions): Promise<number>;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server to hack. _(Optional)_ Hostname/IP of the target server to hack. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Get the chance of successfully hacking a server.
**Signature:** **Signature:**
```typescript ```typescript
hackAnalyzeChance(host: string): number; hackAnalyzeChance(host?: string): number;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
_(Optional)_ Hostname/IP of the target server. The number of threads is limited to the number needed to hack the server's maximum amount of money. _(Optional)_ Hostname/IP of the target server. Optional. If unspecified, the threads are not capped.
</td></tr> </td></tr>
@@ -74,5 +74,5 @@ The security increase.
RAM cost: 1 GB RAM cost: 1 GB
Returns the security increase that would occur if a hack with this many threads happened. Returns the security increase that would occur if a hack with this many threads happened. The number of threads is limited to the number needed to hack the server's maximum amount of money.
+1 -1
View File
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname of the target server to analyze. Hostname/IP of the target server to analyze.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Check if you have root access on a server.
**Signature:** **Signature:**
```typescript ```typescript
hasRootAccess(host: string): boolean; hasRootAccess(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Runs HTTPWorm.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
httpworm(host: string): boolean; httpworm(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+1 -1
View File
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
_(Optional)_ Hostname/IP of the server on which to kill all scripts. _(Optional)_ Hostname/IP of the server on which to kill all scripts. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Runs NUKE.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
nuke(host: string): boolean; nuke(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Runs relaySMTP.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
relaysmtp(host: string): boolean; relaysmtp(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Kill all scripts with a filename.
**Signature:** **Signature:**
```typescript ```typescript
scriptKill(script: string, host: string): boolean; scriptKill(script: string, host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Check if any script with a filename is running.
**Signature:** **Signature:**
```typescript ```typescript
scriptRunning(script: string, host: string): boolean; scriptRunning(script: string, host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -58,7 +58,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Returns a boolean denoting whether or not the specified server exists.
**Signature:** **Signature:**
```typescript ```typescript
serverExists(host: string): boolean; serverExists(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Runs SQLInject.exe on a server.
**Signature:** **Signature:**
```typescript ```typescript
sqlinject(host: string): boolean; sqlinject(host?: string): boolean;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server. _(Optional)_ Hostname/IP of the target server. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+2 -2
View File
@@ -9,7 +9,7 @@ Reduce a server's security level.
**Signature:** **Signature:**
```typescript ```typescript
weaken(host: string, opts?: BasicHGWOptions): Promise<number>; weaken(host?: string, opts?: BasicHGWOptions): Promise<number>;
``` ```
## Parameters ## Parameters
@@ -42,7 +42,7 @@ string
</td><td> </td><td>
Hostname/IP of the target server to weaken. _(Optional)_ Hostname/IP of the target server to weaken. Optional. Defaults to current server if not provided.
</td></tr> </td></tr>
+4 -4
View File
@@ -178,7 +178,10 @@ export const deleteDarknetServer = (server: DarknetServer, force = false): void
DarknetState.Network[server.depth][server.leftOffset] = null; DarknetState.Network[server.depth][server.leftOffset] = null;
} }
if (!isLabyrinth) { if (!isLabyrinth) {
DarknetState.offlineServers.push(server.hostname); // Adding the ip first is an optimization to improve the random distribution
// of reused hostnames slightly. See the comment in generateDarknetServerName.
DarknetState.offlineServers.add(server.ip);
DarknetState.offlineServers.add(server.hostname);
} }
DeleteServer(server.hostname); DeleteServer(server.hostname);
const serverState = getServerState(server.hostname); const serverState = getServerState(server.hostname);
@@ -192,9 +195,6 @@ export const addRandomDarknetServers = (count = 1, difficulty?: number, fixedDep
const newServer = createDarknetServer(diff, -1, -1); const newServer = createDarknetServer(diff, -1, -1);
const range = fixedDepth ? 0 : 3; const range = fixedDepth ? 0 : 3;
moveDarknetServer(newServer, range, range); moveDarknetServer(newServer, range, range);
if (DarknetState.offlineServers.includes(newServer.hostname)) {
DarknetState.offlineServers = DarknetState.offlineServers.filter((s) => s !== newServer.hostname);
}
} }
}; };
+13 -16
View File
@@ -5,7 +5,6 @@ import { helpers } from "../../Netscript/NetscriptHelpers";
import { errorMessage } from "../../Netscript/ErrorMessages"; import { errorMessage } from "../../Netscript/ErrorMessages";
import type { BaseServer } from "../../Server/BaseServer"; import type { BaseServer } from "../../Server/BaseServer";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { DarknetState } from "../models/DarknetState";
import { GenericResponseMessage, ResponseCodeEnum } from "../Enums"; import { GenericResponseMessage, ResponseCodeEnum } from "../Enums";
import { getBackdooredDarkwebServers } from "../utils/darknetNetworkUtils"; import { getBackdooredDarkwebServers } from "../utils/darknetNetworkUtils";
import { hasDarknetAccess } from "../utils/darknetAuthUtils"; import { hasDarknetAccess } from "../utils/darknetAuthUtils";
@@ -19,6 +18,10 @@ type CheckDarknetServerOptions = {
requireSession?: boolean; requireSession?: boolean;
requireDirectConnection?: boolean; requireDirectConnection?: boolean;
preventUseOnStationaryServers?: boolean; preventUseOnStationaryServers?: boolean;
/**
* If you use this option, the server property in the result object is only guaranteed to be a BaseServer, not a
* DarknetServer as the result type shows.
*/
allowNonDarknet?: boolean; allowNonDarknet?: boolean;
backdoorBypasses?: boolean; backdoorBypasses?: boolean;
}; };
@@ -36,27 +39,22 @@ export function expectDarknetAccess(ctx: NetscriptContext): void {
export function checkDarknetServer( export function checkDarknetServer(
ctx: NetscriptContext, ctx: NetscriptContext,
host: string, _host: string,
options: CheckDarknetServerOptions = {}, options: CheckDarknetServerOptions = {},
): ):
| { success: true; code: DarknetResponseCode; message: string; server: DarknetServer } | { success: true; code: DarknetResponseCode; message: string; server: DarknetServer }
| { success: false; code: DarknetResponseCode; message: string } { | { success: false; code: DarknetResponseCode; message: string } {
const currentServer = ctx.workerScript.getServer(); const currentServer = ctx.workerScript.getServer();
const targetServer = GetServer(host); const [targetServer, host] = helpers.getServer(ctx, _host);
if (!targetServer) { if (!targetServer) {
if (DarknetState.offlineServers.includes(host)) {
// Because servers going offline is timing-sensitive, it is outside of // Because servers going offline is timing-sensitive, it is outside of
// player's control. So we don't want to throw for "server does not exist" in this case, // player's control. So we don't want to throw for "server does not exist" in this case,
// despite throwing being the usual doctrine. // despite throwing being the usual doctrine.
logger(ctx)(`Server ${host} is offline.`);
return { return {
success: false, success: false,
code: ResponseCodeEnum.ServiceUnavailable, code: ResponseCodeEnum.ServiceUnavailable,
message: GenericResponseMessage.ServiceUnavailable, message: GenericResponseMessage.ServiceUnavailable,
}; };
} else {
throw errorMessage(ctx, `Server ${host} does not exist.`);
}
} }
const success = { const success = {
success: true, success: true,
@@ -67,19 +65,18 @@ export function checkDarknetServer(
if (!(targetServer instanceof DarknetServer)) { if (!(targetServer instanceof DarknetServer)) {
if (options.allowNonDarknet) { if (options.allowNonDarknet) {
// The return is off-shape here: server is of type DarknetServer, but // The return is off-shape here: server is of type DarknetServer, but
// we've explicitly validated that it's only a BaseServer. Callers // we've explicitly validated that it's only a BaseServer. It's still OK
// using allowNonDarknet should call GetServer on their own for proper // for callers to use this, as long as they are only expecting a BaseServer.
// type-safety, instead of using the server field.
return success; return success;
} }
const result = `${targetServer.hostname} is not a darknet server.`; const result = `${host} is not a darknet server.`;
throw errorMessage(ctx, result); throw errorMessage(ctx, result);
} }
// This is down here because we don't require darknet access for using // This is down here because we don't require darknet access for using
// allowNonDarknet APIs on non-darknet servers. // allowNonDarknet APIs on non-darknet servers.
expectDarknetAccess(ctx); expectDarknetAccess(ctx);
if (options.preventUseOnStationaryServers && targetServer.isStationary) { if (options.preventUseOnStationaryServers && targetServer.isStationary) {
const result = `${targetServer.hostname} is not a valid target: it is a stationary server.`; const result = `${host} is not a valid target: it is a stationary server.`;
throw errorMessage(ctx, result); throw errorMessage(ctx, result);
} }
if ( if (
@@ -87,7 +84,7 @@ export function checkDarknetServer(
!isDirectConnected(currentServer, targetServer) && !isDirectConnected(currentServer, targetServer) &&
!(options.backdoorBypasses && targetServer.backdoorInstalled) !(options.backdoorBypasses && targetServer.backdoorInstalled)
) { ) {
let result = `${targetServer.hostname} is not connected to the current server ${currentServer.hostname}. It may have moved.`; let result = `${host} is not connected to the current server ${currentServer.hostname}. It may have moved.`;
if (options.backdoorBypasses) { if (options.backdoorBypasses) {
result += " You can also use a backdoor or stasis link on the target to allow remote access."; result += " You can also use a backdoor or stasis link on the target to allow remote access.";
} }
@@ -103,7 +100,7 @@ export function checkDarknetServer(
return success; return success;
} }
if (options.requireAdminRights && !targetServer.hasAdminRights) { if (options.requireAdminRights && !targetServer.hasAdminRights) {
const result = `${targetServer.hostname} requires root access. Use ns.dnet.authenticate() to gain access.`; const result = `${host} requires root access. Use ns.dnet.authenticate() to gain access.`;
logger(ctx)(result); logger(ctx)(result);
return { return {
success: false, success: false,
@@ -116,7 +113,7 @@ export function checkDarknetServer(
host !== (!isIPAddress(host) ? currentServer.hostname : currentServer.ip) && host !== (!isIPAddress(host) ? currentServer.hostname : currentServer.ip) &&
!isAuthenticated(targetServer, ctx.workerScript.pid) !isAuthenticated(targetServer, ctx.workerScript.pid)
) { ) {
const result = `${targetServer.hostname} requires a session to do that. Use ns.dnet.connectToSession() first to authenticate with that server.`; const result = `${host} requires a session to do that. Use ns.dnet.connectToSession() first to authenticate with that server.`;
logger(ctx)(result); logger(ctx)(result);
return { return {
success: false, success: false,
+28 -7
View File
@@ -16,6 +16,7 @@ import { DarknetState } from "./DarknetState";
import { getRamBlock } from "../effects/ramblock"; import { getRamBlock } from "../effects/ramblock";
import { hasFullDarknetAccess } from "../effects/effects"; import { hasFullDarknetAccess } from "../effects/effects";
import { getFriendlyType, TypeAssertionError } from "../../utils/TypeAssertion"; import { getFriendlyType, TypeAssertionError } from "../../utils/TypeAssertion";
import { isIPAddress } from "../../Types/strings";
export type PasswordResponse = { export type PasswordResponse = {
code: DarknetResponseCode; code: DarknetResponseCode;
@@ -86,23 +87,43 @@ export const DnetServerBuilder = (options: DarknetServerOptions): DarknetServer
isStationary: false, isStationary: false,
}); });
server.updateRamUsed(ramBlock); server.updateRamUsed(ramBlock);
removeFromOfflineServers(name); DarknetState.offlineServers.delete(name);
DarknetState.offlineServers.delete(server.ip);
AddToAllServers(server); AddToAllServers(server);
return server; return server;
}; };
export const generateDarknetServerName = (): string => { export const generateDarknetServerName = (): string => {
if (Math.random() < 0.03 && DarknetState.offlineServers.length > 0 && hasFullDarknetAccess()) { if (Math.random() < 0.03 && DarknetState.offlineServers.size > 0 && hasFullDarknetAccess()) {
return DarknetState.offlineServers[Math.floor(Math.random() * DarknetState.offlineServers.length)]; // Reuse a hostname that went offline. Note that we're only reusing hostnames,
// not IPs. This asymmetry is intentional - people find hostnames easier to
// work with, and this adds a bit of friction to compensate.
// Use an iterator to directly skip to the appropriate position. Sets don't
// have a way to index by offset, and converting to an array would be wasteful.
const offset = Math.floor(Math.random() * DarknetState.offlineServers.size);
const it = DarknetState.offlineServers.values();
for (let i = 0; i < offset; ++i) {
it.next();
}
// The set contains both IPs and hostnames. If we hit an IP, keep going
// forward until we find a hostname. *If* the Set implements traversal in
// the same order as insertion, then the fact that we insert IPs first
// means that we will always find a hostname and the sampling will be
// unbiased. Otherwise, it will be Mostly Unbiased(TM), and in rare cases
// we will fall off the end without reusing a name.
let serverName = it.next().value;
while (serverName != null && isIPAddress(serverName)) {
serverName = it.next().value;
}
if (serverName != null) {
return serverName;
}
} }
return decorateName(getBaseName()); return decorateName(getBaseName());
}; };
export const removeFromOfflineServers = (hostname: string): void => {
DarknetState.offlineServers = DarknetState.offlineServers.filter((server) => server !== hostname);
};
const getBaseName = (): string => { const getBaseName = (): string => {
if (Math.random() < 0.05) { if (Math.random() < 0.05) {
return commonPasswordDictionary[Math.floor(Math.random() * commonPasswordDictionary.length)]; return commonPasswordDictionary[Math.floor(Math.random() * commonPasswordDictionary.length)];
+18 -2
View File
@@ -53,7 +53,23 @@ export const DarknetState = {
* Do NOT access the server state directly via this property. You must call getServerState. * Do NOT access the server state directly via this property. You must call getServerState.
*/ */
serverState: new Map<string, ServerState>(), serverState: new Map<string, ServerState>(),
offlineServers: [] as string[], /**
* Much like AllServers, this contains both hostnames and IP addresses in a single set.
*
* A note about memory use: On Chrome 144.0.7559.133, memory tests indicate that a
* dense array takes ~4 bytes/entry. A Set takes ~10.24 bytes/entry. And the
* strings we are storing inside take 14 + length bytes each. Random IPs
* average (10+90*2+156*3)*4/256+3 = 13.28 chars, so 27.28 bytes. The
* hostnames I don't know how to estimate, but same ballpark.
*
* There's two points to this:
* 1. Most of the cost comes from the strings, so using an array won't save
* much compared to a Set. It's better to use a Set for fast lookups.
* 2. The size of this Set can grow without bound over time, so it's
* important that servers not get deleted too aggressively. A
* conservative estimate is 80 bytes/server, since there are two entries.
*/
offlineServers: new Set<string>(),
showFullNetwork: false, showFullNetwork: false,
zoomIndex: 7, zoomIndex: 7,
netViewTopScroll: 0, netViewTopScroll: 0,
@@ -76,7 +92,7 @@ export function prestigeDarknetState(prestigeSourceFile: boolean): void {
DarknetState.stockPromotions = {}; DarknetState.stockPromotions = {};
DarknetState.migrationInductionServers.clear(); DarknetState.migrationInductionServers.clear();
DarknetState.serverState.clear(); DarknetState.serverState.clear();
DarknetState.offlineServers = []; DarknetState.offlineServers.clear();
} }
/** /**
@@ -41,7 +41,7 @@ In some cases, the only way to get to deeper parts of the net is to hitch a ride
**Connected** - Each server on the network has specific other servers it is linked to. These are the links seen in the UI, the servers that appear when using "scan" in the terminal, or that can be seen by calling dnet.probe. This kind of direct connection is required for most dnet API methods. ns.exec requires either a direct connection, or a backdoor, or a stasis link (which also sets a backdoor) to target a darknet server. **Connected** - Each server on the network has specific other servers it is linked to. These are the links seen in the UI, the servers that appear when using "scan" in the terminal, or that can be seen by calling dnet.probe. This kind of direct connection is required for most dnet API methods. ns.exec requires either a direct connection, or a backdoor, or a stasis link (which also sets a backdoor) to target a darknet server.
**Offline** - Sometimes darknet servers will go offline. Effectively, the server is deleted, and any running scripts that were on it are killed. Eventually, a server with the same name may come back online, but it will have a different password, and will be fully cleaned and cleared of scripts. **Offline** - Sometimes darknet servers will go offline. Effectively, the server is deleted, and any running scripts that were on it are killed. Eventually, a server with the same name may come back online, but it will have a different password, and will be fully cleaned and cleared of scripts. Using an API call targeting an offline server will produce a default/failed response instead of the exception you typically get with non-existent servers. Take care with this, since the list of offline servers is cleared on prestige and game reload.
### Darknet script design considerations ### Darknet script design considerations
+33 -28
View File
@@ -67,6 +67,7 @@ import { Settings } from "../Settings/Settings";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { getRecordKeys } from "../Types/Record"; import { getRecordKeys } from "../Types/Record";
import { DarknetServer } from "../Server/DarknetServer"; import { DarknetServer } from "../Server/DarknetServer";
import { DarknetState } from "../DarkNet/models/DarknetState";
import { getFriendlyType } from "../utils/TypeAssertion"; import { getFriendlyType } from "../utils/TypeAssertion";
export const helpers = { export const helpers = {
@@ -474,22 +475,17 @@ function updateDynamicRam(ctx: NetscriptContext, ramCost: number): void {
} }
} }
function scriptIdentifier( function scriptIdentifier(ctx: NetscriptContext, scriptID: unknown, _host: unknown, _args: unknown): ScriptIdentifier {
ctx: NetscriptContext,
scriptID: unknown,
_hostname: unknown,
_args: unknown,
): ScriptIdentifier {
const ws = ctx.workerScript; const ws = ctx.workerScript;
// Provide the pid for the current script if no identifier provided // Provide the pid for the current script if no identifier provided
if (scriptID === undefined) return ws.pid; if (scriptID === undefined) return ws.pid;
if (typeof scriptID === "number") return scriptID; if (typeof scriptID === "number") return scriptID;
if (typeof scriptID === "string") { if (typeof scriptID === "string") {
const hostname = _hostname === undefined ? ctx.workerScript.hostname : string(ctx, "hostname", _hostname); const host = _host === undefined ? ctx.workerScript.hostname : string(ctx, "host", _host);
const args = _args === undefined ? [] : scriptArgs(ctx, _args); const args = _args === undefined ? [] : scriptArgs(ctx, _args);
return { return {
scriptname: scriptID, scriptname: scriptID,
hostname, host,
args, args,
}; };
} }
@@ -501,26 +497,34 @@ function scriptIdentifier(
* server (e.g., pre-TOR darkweb, pre-TRP WD). * server (e.g., pre-TOR darkweb, pre-TRP WD).
* *
* @param {NetscriptContext} ctx - Context from which getServer is being called. For logging purposes. * @param {NetscriptContext} ctx - Context from which getServer is being called. For logging purposes.
* @param {string} hostname - Hostname of the server * @param {unknown} _host - Hostname or ip of the server, defaults to current server
* @returns {BaseServer} The specified server as a BaseServer * @returns {[BaseServer | null, string]} A pair containing the specified server as a BaseServer, or
* null if the server is offline. The second part is the resolved hostname/ip.
*/ */
export function getServer(ctx: NetscriptContext, hostname: string): BaseServer { export function getServer(ctx: NetscriptContext, _host: unknown): [BaseServer | null, string] {
const server = GetServer(hostname); const host = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname);
if (server == null || (server.serversOnNetwork.length == 0 && !(server instanceof DarknetServer))) { const server = GetServer(host);
const str = hostname === "" ? "'' (empty string)" : "'" + hostname + "'"; if (server != null && (server.serversOnNetwork.length > 0 || server instanceof DarknetServer)) {
throw errorMessage(ctx, `Invalid hostname: ${str}`); return [server, host];
} }
return server; if (DarknetState.offlineServers.has(host)) {
log(ctx, () => `Server ${host} is offline.`);
return [null, host];
}
const str = host === "" ? "'' (empty string)" : "'" + host + "'";
throw errorMessage(ctx, `Invalid host: ${str}`);
} }
/** /**
* A "normal server" is an instance of the Server class in src/Server/Server.ts. * A "normal server" is an instance of the Server class in src/Server/Server.ts.
*/ */
function getNormalServer(ctx: NetscriptContext, host: string): Server { function getNormalServer(ctx: NetscriptContext, _host: unknown): Server {
const server = getServer(ctx, host); const [server, host] = getServer(ctx, _host);
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
let errorMessage = `Cannot be executed on ${host}.`; let errorMessage = `Cannot be executed on ${host}.`;
if (server instanceof HacknetServer) { if (server == null) {
errorMessage += " The server was offline (and thus a darknet server).";
} else if (server instanceof HacknetServer) {
errorMessage += " The server must not be a hacknet server."; errorMessage += " The server must not be a hacknet server.";
} else if (server instanceof DarknetServer) { } else if (server instanceof DarknetServer) {
errorMessage += " The server must not be a darknet server."; errorMessage += " The server must not be a darknet server.";
@@ -530,10 +534,10 @@ function getNormalServer(ctx: NetscriptContext, host: string): Server {
return server; return server;
} }
function hack(ctx: NetscriptContext, hostname: string, manual: boolean, opts: unknown): Promise<number> { function hack(ctx: NetscriptContext, _host: unknown, manual: boolean, opts: unknown): Promise<number> {
const ws = ctx.workerScript; const ws = ctx.workerScript;
const { threads, stock, additionalMsec } = validateHGWOptions(ctx, opts); const { threads, stock, additionalMsec } = validateHGWOptions(ctx, opts);
const server = getNormalServer(ctx, hostname); const server = getNormalServer(ctx, _host);
// Calculate the hacking time // Calculate the hacking time
// This is in seconds // This is in seconds
@@ -737,7 +741,7 @@ export function scriptPath(
* Searches for and returns the RunningScript objects for the specified script. * Searches for and returns the RunningScript objects for the specified script.
* If the 'fn' argument is not specified, this returns the current RunningScript. * If the 'fn' argument is not specified, this returns the current RunningScript.
* @param fn - Filename of script * @param fn - Filename of script
* @param hostname - Hostname/ip of the server on which the script resides * @param host - Hostname/ip of the server on which the script resides
* @param scriptArgs - Running script's arguments * @param scriptArgs - Running script's arguments
* @returns Running scripts identified by the parameters, or empty if no such script * @returns Running scripts identified by the parameters, or empty if no such script
* exists, or only the current running script if the first argument 'fn' * exists, or only the current running script if the first argument 'fn'
@@ -746,7 +750,7 @@ export function scriptPath(
export function getRunningScriptsByArgs( export function getRunningScriptsByArgs(
ctx: NetscriptContext, ctx: NetscriptContext,
fn: string, fn: string,
hostname: string, host: string,
scriptArgs: ScriptArg[], scriptArgs: ScriptArg[],
): Map<number, RunningScript> | null { ): Map<number, RunningScript> | null {
if (!Array.isArray(scriptArgs)) { if (!Array.isArray(scriptArgs)) {
@@ -759,10 +763,11 @@ export function getRunningScriptsByArgs(
const path = scriptPath(ctx, "filename", fn); const path = scriptPath(ctx, "filename", fn);
// Lookup server to scope search // Lookup server to scope search
if (hostname == null) { if (host == null) {
hostname = ctx.workerScript.hostname; host = ctx.workerScript.hostname;
} }
const server = helpers.getServer(ctx, hostname); const [server] = helpers.getServer(ctx, host);
if (!server) return null;
return findRunningScripts(path, scriptArgs, server); return findRunningScripts(path, scriptArgs, server);
} }
@@ -771,7 +776,7 @@ function getRunningScript(ctx: NetscriptContext, ident: ScriptIdentifier): Runni
if (typeof ident === "number") { if (typeof ident === "number") {
return findRunningScriptByPid(ident); return findRunningScriptByPid(ident);
} else { } else {
const scripts = getRunningScriptsByArgs(ctx, ident.scriptname, ident.hostname, ident.args); const scripts = getRunningScriptsByArgs(ctx, ident.scriptname, ident.host, ident.args);
if (scripts === null) { if (scripts === null) {
return null; return null;
} }
@@ -789,7 +794,7 @@ function getRunningScript(ctx: NetscriptContext, ident: ScriptIdentifier): Runni
function getCannotFindRunningScriptErrorMessage(ident: ScriptIdentifier): string { function getCannotFindRunningScriptErrorMessage(ident: ScriptIdentifier): string {
if (typeof ident === "number") return `Cannot find running script with pid: ${ident}`; if (typeof ident === "number") return `Cannot find running script with pid: ${ident}`;
return `Cannot find running script ${ident.scriptname} on server ${ident.hostname} with args: ${arrayToString( return `Cannot find running script ${ident.scriptname} on server ${ident.host} with args: ${arrayToString(
ident.args, ident.args,
)}`; )}`;
} }
+1 -1
View File
@@ -5,6 +5,6 @@ export type ScriptIdentifier =
| number | number
| { | {
scriptname: string; scriptname: string;
hostname: string; host: string;
args: ScriptArg[]; args: ScriptArg[];
}; };
+125 -169
View File
@@ -109,7 +109,6 @@ import { isIPAddress } from "./Types/strings";
import { compile } from "./NetscriptJSEvaluator"; import { compile } from "./NetscriptJSEvaluator";
import { Script } from "./Script/Script"; import { Script } from "./Script/Script";
import { NetscriptFormat } from "./NetscriptFunctions/Format"; import { NetscriptFormat } from "./NetscriptFunctions/Format";
import { DarknetState } from "./DarkNet/models/DarknetState";
import { checkDarknetServer } from "./DarkNet/effects/offlineServerHandling"; import { checkDarknetServer } from "./DarkNet/effects/offlineServerHandling";
import { DarknetServer } from "./Server/DarknetServer"; import { DarknetServer } from "./Server/DarknetServer";
import { FragmentTypeEnum } from "./CotMG/FragmentType"; import { FragmentTypeEnum } from "./CotMG/FragmentType";
@@ -176,10 +175,12 @@ export const ns: InternalAPI<NSFull> = {
return vsprintf(format, _args); return vsprintf(format, _args);
}, },
scan: (ctx) => (_host, _returnOpts) => { scan: (ctx) => (_host, _returnOpts) => {
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname;
const returnOpts = helpers.hostReturnOptions(_returnOpts); const returnOpts = helpers.hostReturnOptions(_returnOpts);
const server = helpers.getServer(ctx, host); const [server, host] = helpers.getServer(ctx, _host);
const out: string[] = []; const out: string[] = [];
if (!server) {
return out;
}
for (let i = 0; i < server.serversOnNetwork.length; i++) { for (let i = 0; i < server.serversOnNetwork.length; i++) {
const s = getServerOnNetwork(server, i); const s = getServerOnNetwork(server, i);
if (s === null || s instanceof DarknetServer) continue; if (s === null || s instanceof DarknetServer) continue;
@@ -187,20 +188,18 @@ export const ns: InternalAPI<NSFull> = {
if (entry === null) continue; if (entry === null) continue;
out.push(entry); out.push(entry);
} }
helpers.log(ctx, () => `returned ${out.length} connections for ${isIPAddress(host) ? server.ip : server.hostname}`); helpers.log(ctx, () => `returned ${out.length} connections for ${host}`);
return out; return out;
}, },
hasTorRouter: () => () => Player.hasTorRouter(), hasTorRouter: () => () => Player.hasTorRouter(),
hack: (ctx) => (_host, opts?) => { hack: (ctx) => (_host?, opts?) => {
const host = helpers.string(ctx, "host", _host); return helpers.hack(ctx, _host, false, opts);
return helpers.hack(ctx, host, false, opts);
}, },
hackAnalyzeThreads: (ctx) => (_host, _hackAmount) => { hackAnalyzeThreads: (ctx) => (_host, _hackAmount) => {
const host = helpers.string(ctx, "host", _host);
const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount); const hackAmount = helpers.number(ctx, "hackAmount", _hackAmount);
// Check argument validity // Check argument validity
const server = helpers.getNormalServer(ctx, host); const server = helpers.getNormalServer(ctx, _host);
if (isNaN(hackAmount)) { if (isNaN(hackAmount)) {
throw helpers.errorMessage( throw helpers.errorMessage(
ctx, ctx,
@@ -222,18 +221,15 @@ export const ns: InternalAPI<NSFull> = {
return hackAmount / (server.moneyAvailable * percentHacked); return hackAmount / (server.moneyAvailable * percentHacked);
}, },
hackAnalyze: (ctx) => (_host) => { hackAnalyze: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
return calculatePercentMoneyHacked(server, Player); return calculatePercentMoneyHacked(server, Player);
}, },
hackAnalyzeSecurity: (ctx) => (_threads, _host?) => { hackAnalyzeSecurity: (ctx) => (_threads, _host?) => {
let threads = helpers.number(ctx, "threads", _threads); let threads = helpers.number(ctx, "threads", _threads);
if (_host) { if (_host) {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
const percentHacked = calculatePercentMoneyHacked(server, Player); const percentHacked = calculatePercentMoneyHacked(server, Player);
@@ -245,10 +241,8 @@ export const ns: InternalAPI<NSFull> = {
return ServerConstants.ServerFortifyAmount * threads; return ServerConstants.ServerFortifyAmount * threads;
}, },
hackAnalyzeChance: (ctx) => (_host) => { hackAnalyzeChance: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
return calculateHackingChance(server, Player); return calculateHackingChance(server, Player);
}, },
@@ -269,10 +263,9 @@ export const ns: InternalAPI<NSFull> = {
return new Promise((resolve) => setTimeout(() => resolve(true), time)); return new Promise((resolve) => setTimeout(() => resolve(true), time));
}, },
grow: (ctx) => (_host, opts?) => { grow: (ctx) => (_host, opts?) => {
const host = helpers.string(ctx, "host", _host);
const { threads, stock, additionalMsec } = helpers.validateHGWOptions(ctx, opts); const { threads, stock, additionalMsec } = helpers.validateHGWOptions(ctx, opts);
const server = helpers.getNormalServer(ctx, host); const server = helpers.getNormalServer(ctx, _host);
// No root access or skill level too low // No root access or skill level too low
const canHack = netscriptCanGrow(server); const canHack = netscriptCanGrow(server);
@@ -317,12 +310,11 @@ export const ns: InternalAPI<NSFull> = {
growthAnalyze: growthAnalyze:
(ctx) => (ctx) =>
(_host, _multiplier, _cores = 1) => { (_host, _multiplier, _cores = 1) => {
const host = helpers.string(ctx, "hostname", _host);
const mult = helpers.number(ctx, "multiplier", _multiplier); const mult = helpers.number(ctx, "multiplier", _multiplier);
const cores = helpers.positiveInteger(ctx, "cores", _cores); const cores = helpers.positiveInteger(ctx, "cores", _cores);
// Check argument validity // Check argument validity
const server = helpers.getNormalServer(ctx, host); const server = helpers.getNormalServer(ctx, _host);
if (!Number.isFinite(mult) || mult < 1) { if (!Number.isFinite(mult) || mult < 1) {
throw helpers.errorMessage(ctx, `Invalid argument: multiplier must be finite and >= 1, is ${mult}.`); throw helpers.errorMessage(ctx, `Invalid argument: multiplier must be finite and >= 1, is ${mult}.`);
} }
@@ -335,8 +327,7 @@ export const ns: InternalAPI<NSFull> = {
let threads = helpers.number(ctx, "threads", _threads); let threads = helpers.number(ctx, "threads", _threads);
if (_host) { if (_host) {
const cores = helpers.number(ctx, "cores", _cores); const cores = helpers.number(ctx, "cores", _cores);
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
const maxThreadsNeeded = Math.ceil( const maxThreadsNeeded = Math.ceil(
numCycleForGrowthCorrected(server, server.moneyMax, server.moneyAvailable, cores), numCycleForGrowthCorrected(server, server.moneyMax, server.moneyAvailable, cores),
@@ -347,11 +338,10 @@ export const ns: InternalAPI<NSFull> = {
return 2 * ServerConstants.ServerFortifyAmount * threads; return 2 * ServerConstants.ServerFortifyAmount * threads;
}, },
weaken: (ctx) => async (_host, opts?) => { weaken: (ctx) => async (_host?, opts?) => {
const host = helpers.string(ctx, "host", _host);
const { threads, additionalMsec } = helpers.validateHGWOptions(ctx, opts); const { threads, additionalMsec } = helpers.validateHGWOptions(ctx, opts);
const server = helpers.getNormalServer(ctx, host); const server = helpers.getNormalServer(ctx, _host);
// No root access or skill level too low // No root access or skill level too low
const canHack = netscriptCanWeaken(server); const canHack = netscriptCanWeaken(server);
@@ -404,7 +394,7 @@ export const ns: InternalAPI<NSFull> = {
const threads = ctx.workerScript.scriptRef.threads; const threads = ctx.workerScript.scriptRef.threads;
const hostname = ctx.workerScript.hostname; const hostname = ctx.workerScript.hostname;
helpers.log(ctx, () => `Sharing ${threads} threads on ${hostname}.`); helpers.log(ctx, () => `Sharing ${threads} threads on ${hostname}.`);
const end = startSharing(threads, helpers.getServer(ctx, hostname).cpuCores); const end = startSharing(threads, ctx.workerScript.getServer().cpuCores);
return helpers.netscriptDelay(ctx, ShareBonusTime).finally(function () { return helpers.netscriptDelay(ctx, ShareBonusTime).finally(function () {
helpers.log(ctx, () => `Finished sharing ${threads} threads on ${hostname}.`); helpers.log(ctx, () => `Finished sharing ${threads} threads on ${hostname}.`);
end(); end();
@@ -537,10 +527,8 @@ export const ns: InternalAPI<NSFull> = {
return runningScriptObj.logs.map((x) => String(x)); return runningScriptObj.logs.map((x) => String(x));
}, },
nuke: (ctx) => (_host) => { nuke: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (server.hasAdminRights) { if (server.hasAdminRights) {
helpers.log(ctx, () => `Already have root access to '${server.hostname}'.`); helpers.log(ctx, () => `Already have root access to '${server.hostname}'.`);
return true; return true;
@@ -557,9 +545,8 @@ export const ns: InternalAPI<NSFull> = {
helpers.log(ctx, () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); helpers.log(ctx, () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`);
return true; return true;
}, },
brutessh: (ctx) => (_host) => { brutessh: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (!Player.hasProgram(CompletedProgramName.bruteSsh)) { if (!Player.hasProgram(CompletedProgramName.bruteSsh)) {
helpers.log(ctx, () => "You do not have the BruteSSH.exe program!"); helpers.log(ctx, () => "You do not have the BruteSSH.exe program!");
return false; return false;
@@ -573,9 +560,8 @@ export const ns: InternalAPI<NSFull> = {
} }
return true; return true;
}, },
ftpcrack: (ctx) => (_host) => { ftpcrack: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (!Player.hasProgram(CompletedProgramName.ftpCrack)) { if (!Player.hasProgram(CompletedProgramName.ftpCrack)) {
helpers.log(ctx, () => "You do not have the FTPCrack.exe program!"); helpers.log(ctx, () => "You do not have the FTPCrack.exe program!");
return false; return false;
@@ -590,8 +576,7 @@ export const ns: InternalAPI<NSFull> = {
return true; return true;
}, },
relaysmtp: (ctx) => (_host) => { relaysmtp: (ctx) => (_host) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (!Player.hasProgram(CompletedProgramName.relaySmtp)) { if (!Player.hasProgram(CompletedProgramName.relaySmtp)) {
helpers.log(ctx, () => "You do not have the relaySMTP.exe program!"); helpers.log(ctx, () => "You do not have the relaySMTP.exe program!");
return false; return false;
@@ -605,9 +590,8 @@ export const ns: InternalAPI<NSFull> = {
} }
return true; return true;
}, },
httpworm: (ctx) => (_host) => { httpworm: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (!Player.hasProgram(CompletedProgramName.httpWorm)) { if (!Player.hasProgram(CompletedProgramName.httpWorm)) {
helpers.log(ctx, () => "You do not have the HTTPWorm.exe program!"); helpers.log(ctx, () => "You do not have the HTTPWorm.exe program!");
return false; return false;
@@ -621,9 +605,8 @@ export const ns: InternalAPI<NSFull> = {
} }
return true; return true;
}, },
sqlinject: (ctx) => (_host) => { sqlinject: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (!Player.hasProgram(CompletedProgramName.sqlInject)) { if (!Player.hasProgram(CompletedProgramName.sqlInject)) {
helpers.log(ctx, () => "You do not have the SQLInject.exe program!"); helpers.log(ctx, () => "You do not have the SQLInject.exe program!");
return false; return false;
@@ -654,19 +637,17 @@ export const ns: InternalAPI<NSFull> = {
const host = helpers.string(ctx, "host", _host); const host = helpers.string(ctx, "host", _host);
const runOpts = helpers.runOptions(ctx, _thread_or_opt); const runOpts = helpers.runOptions(ctx, _thread_or_opt);
const args = helpers.scriptArgs(ctx, _args); const args = helpers.scriptArgs(ctx, _args);
if ( const serverCheck = checkDarknetServer(ctx, host, {
!checkDarknetServer(ctx, host, {
allowNonDarknet: true, allowNonDarknet: true,
requireAdminRights: true, requireAdminRights: true,
requireSession: true, requireSession: true,
requireDirectConnection: true, requireDirectConnection: true,
backdoorBypasses: true, backdoorBypasses: true,
}).success });
) { if (!serverCheck.success) {
return 0; return 0;
} }
const server = helpers.getServer(ctx, host); return runScriptFromScript("exec", serverCheck.server, path, args, ctx.workerScript, runOpts);
return runScriptFromScript("exec", server, path, args, ctx.workerScript, runOpts);
}, },
spawn: spawn:
(ctx) => (ctx) =>
@@ -713,20 +694,20 @@ export const ns: InternalAPI<NSFull> = {
}, },
kill: kill:
(ctx) => (ctx) =>
(scriptID, host = ctx.workerScript.hostname, ...scriptArgs) => { (scriptID, _host?, ...scriptArgs) => {
const ident = helpers.scriptIdentifier(ctx, scriptID, host, scriptArgs); const ident = helpers.scriptIdentifier(ctx, scriptID, _host, scriptArgs);
let res; let res;
const killByPid = typeof ident === "number"; const killByPid = typeof ident === "number";
if (killByPid) { if (killByPid) {
// Kill by pid // Kill by pid
res = killWorkerScriptByPid(ident, ctx.workerScript); res = killWorkerScriptByPid(ident, ctx.workerScript);
} else { } else {
// Kill by filename/hostname // Kill by filename/host
if (scriptID === undefined) { if (scriptID === undefined) {
throw helpers.errorMessage(ctx, "Usage: kill(scriptname, server, [arg1], [arg2]...)"); throw helpers.errorMessage(ctx, "Usage: kill(scriptname, server, [arg1], [arg2]...)");
} }
const byPid = helpers.getRunningScriptsByArgs(ctx, ident.scriptname, ident.hostname, ident.args); const byPid = helpers.getRunningScriptsByArgs(ctx, ident.scriptname, ident.host, ident.args);
if (byPid === null) { if (byPid === null) {
helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(ident)); helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(ident));
return false; return false;
@@ -742,7 +723,7 @@ export const ns: InternalAPI<NSFull> = {
if (killByPid) { if (killByPid) {
helpers.log(ctx, () => `Killing script with PID ${ident}`); helpers.log(ctx, () => `Killing script with PID ${ident}`);
} else { } else {
helpers.log(ctx, () => `Killing '${scriptID}' on '${host}' with args: ${arrayToString(scriptArgs)}.`); helpers.log(ctx, () => `Killing '${scriptID}' on '${ident.host}' with args: ${arrayToString(scriptArgs)}.`);
} }
return true; return true;
} else { } else {
@@ -751,7 +732,7 @@ export const ns: InternalAPI<NSFull> = {
} else { } else {
helpers.log( helpers.log(
ctx, ctx,
() => `Internal error killing '${scriptID}' on '${host}' with args: ${arrayToString(scriptArgs)}`, () => `Internal error killing '${scriptID}' on '${ident.host}' with args: ${arrayToString(scriptArgs)}`,
); );
} }
return false; return false;
@@ -759,10 +740,10 @@ export const ns: InternalAPI<NSFull> = {
}, },
killall: killall:
(ctx) => (ctx) =>
(_host = ctx.workerScript.hostname, _safetyGuard = true) => { (_host?, _safetyGuard = true) => {
const host = helpers.string(ctx, "host", _host);
const safetyGuard = !!_safetyGuard; const safetyGuard = !!_safetyGuard;
const server = helpers.getServer(ctx, host); const [server, host] = helpers.getServer(ctx, _host);
if (!server) return false;
let scriptsKilled = 0; let scriptsKilled = 0;
for (const byPid of server.runningScriptMap.values()) { for (const byPid of server.runningScriptMap.values()) {
@@ -772,7 +753,7 @@ export const ns: InternalAPI<NSFull> = {
++scriptsKilled; ++scriptsKilled;
} }
} }
helpers.log(ctx, () => `Killing all scripts on '${server.hostname}'.`); helpers.log(ctx, () => `Killing all scripts on '${host}'.`);
return scriptsKilled > 0; return scriptsKilled > 0;
}, },
@@ -784,20 +765,19 @@ export const ns: InternalAPI<NSFull> = {
scp: (ctx) => (_files, _destination, _source) => { scp: (ctx) => (_files, _destination, _source) => {
const destination = helpers.string(ctx, "destination", _destination); const destination = helpers.string(ctx, "destination", _destination);
const source = helpers.string(ctx, "source", _source ?? ctx.workerScript.hostname); const source = helpers.string(ctx, "source", _source ?? ctx.workerScript.hostname);
if ( const destinationCheck = checkDarknetServer(ctx, destination, {
!checkDarknetServer(ctx, destination, {
allowNonDarknet: true, allowNonDarknet: true,
requireAdminRights: true, requireAdminRights: true,
requireSession: true, requireSession: true,
}).success });
) { if (!destinationCheck.success) {
return false; return false;
} }
if (!checkDarknetServer(ctx, source, { allowNonDarknet: true }).success) { const destServer = destinationCheck.server;
const [sourceServer] = helpers.getServer(ctx, source);
if (!sourceServer) {
return false; return false;
} }
const sourceServer = helpers.getServer(ctx, source);
const destServer = helpers.getServer(ctx, destination);
const files = Array.isArray(_files) ? _files : [_files]; const files = Array.isArray(_files) ? _files : [_files];
const lits: FilePath[] = []; const lits: FilePath[] = [];
const contentFiles: ContentFilePath[] = []; const contentFiles: ContentFilePath[] = [];
@@ -857,13 +837,11 @@ export const ns: InternalAPI<NSFull> = {
return noFailures; return noFailures;
}, },
ls: (ctx) => (_host, _substring) => { ls: (ctx) => (_host, _substring) => {
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const substring = helpers.string(ctx, "substring", _substring ?? ""); const substring = helpers.string(ctx, "substring", _substring ?? "");
if (DarknetState.offlineServers.includes(host)) { if (!server) {
helpers.log(ctx, () => `ls failed, because ${host} is offline.`);
return []; return [];
} }
const server = helpers.getServer(ctx, host);
const allFilenames = [ const allFilenames = [
...server.contracts.map((contract) => contract.fn), ...server.contracts.map((contract) => contract.fn),
@@ -883,12 +861,10 @@ export const ns: InternalAPI<NSFull> = {
...helpers.createPublicRunningScript(rs.runningScript), ...helpers.createPublicRunningScript(rs.runningScript),
})); }));
}, },
ps: ps: (ctx) => (_host?) => {
(ctx) => const [server] = helpers.getServer(ctx, _host);
(_host = ctx.workerScript.hostname) => {
const host = helpers.string(ctx, "host", _host);
const server = helpers.getServer(ctx, host);
const processes: ProcessInfo[] = []; const processes: ProcessInfo[] = [];
if (!server) return processes;
for (const byPid of server.runningScriptMap.values()) { for (const byPid of server.runningScriptMap.values()) {
for (const script of byPid.values()) { for (const script of byPid.values()) {
processes.push({ processes.push({
@@ -902,15 +878,14 @@ export const ns: InternalAPI<NSFull> = {
} }
return processes; return processes;
}, },
hasRootAccess: (ctx) => (_host) => { hasRootAccess: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return false;
return server.hasAdminRights; return server.hasAdminRights;
}, },
getHostname: (ctx) => () => ctx.workerScript.hostname, getHostname: (ctx) => () => ctx.workerScript.hostname,
getIP: (ctx) => () => { getIP: (ctx) => () => {
const hostname = ctx.workerScript.hostname; const server = ctx.workerScript.getServer();
const server = helpers.getServer(ctx, hostname);
return server.ip; return server.ip;
}, },
getHackingLevel: (ctx) => () => { getHackingLevel: (ctx) => () => {
@@ -949,23 +924,17 @@ export const ns: InternalAPI<NSFull> = {
return Object.assign({}, getBitNodeMultipliers(n, lvl)); return Object.assign({}, getBitNodeMultipliers(n, lvl));
}, },
getServer: (ctx) => (_host) => { getServer: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname); const [server, host] = helpers.getServer(ctx, _host);
const server = GetServer(host);
// If the target server does not exist
if (!server) { if (!server) {
if (DarknetState.offlineServers.includes(host)) {
// If the server is offline, return a dummy object with isOnline = false. // If the server is offline, return a dummy object with isOnline = false.
helpers.log(ctx, () => `Server ${host} is offline.`); const isIp = isIPAddress(host);
return { return {
isOnline: false, isOnline: false,
...exampleDarknetServerData, ...exampleDarknetServerData,
hostname: host, hostname: isIp ? "" : host,
ip: isIp ? host : "",
} satisfies DarknetServerData & { isOnline: boolean }; } satisfies DarknetServerData & { isOnline: boolean };
} else {
// Throw, otherwise.
throw helpers.errorMessage(ctx, `Server ${host} does not exist.`);
}
} }
if (server instanceof DarknetServer) { if (server instanceof DarknetServer) {
return { return {
@@ -1022,9 +991,8 @@ export const ns: InternalAPI<NSFull> = {
serverGrowth: server.serverGrowth, serverGrowth: server.serverGrowth,
} satisfies NSInterfaceServer; } satisfies NSInterfaceServer;
}, },
getServerMoneyAvailable: (ctx) => (_host) => { getServerMoneyAvailable: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
if (server.hostname == "home") { if (server.hostname == "home") {
// Return player's money // Return player's money
helpers.log(ctx, () => `returned player's money: ${formatMoney(Player.money)}`); helpers.log(ctx, () => `returned player's money: ${formatMoney(Player.money)}`);
@@ -1033,63 +1001,56 @@ export const ns: InternalAPI<NSFull> = {
helpers.log(ctx, () => `returned ${formatMoney(server.moneyAvailable)} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${formatMoney(server.moneyAvailable)} for '${server.hostname}'`);
return server.moneyAvailable; return server.moneyAvailable;
}, },
getServerSecurityLevel: (ctx) => (_host) => { getServerSecurityLevel: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${formatSecurity(server.hackDifficulty)} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${formatSecurity(server.hackDifficulty)} for '${server.hostname}'`);
return server.hackDifficulty; return server.hackDifficulty;
}, },
getServerBaseSecurityLevel: (ctx) => (_host) => { getServerBaseSecurityLevel: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${formatSecurity(server.baseDifficulty)} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${formatSecurity(server.baseDifficulty)} for '${server.hostname}'`);
return server.baseDifficulty; return server.baseDifficulty;
}, },
getServerMinSecurityLevel: (ctx) => (_host) => { getServerMinSecurityLevel: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${formatSecurity(server.minDifficulty)} for ${server.hostname}`); helpers.log(ctx, () => `returned ${formatSecurity(server.minDifficulty)} for ${server.hostname}`);
return server.minDifficulty; return server.minDifficulty;
}, },
getServerRequiredHackingLevel: (ctx) => (_host) => { getServerRequiredHackingLevel: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${formatNumberNoSuffix(server.requiredHackingSkill, 0)} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${formatNumberNoSuffix(server.requiredHackingSkill, 0)} for '${server.hostname}'`);
return server.requiredHackingSkill; return server.requiredHackingSkill;
}, },
getServerMaxMoney: (ctx) => (_host) => { getServerMaxMoney: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${formatMoney(server.moneyMax)} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${formatMoney(server.moneyMax)} for '${server.hostname}'`);
return server.moneyMax; return server.moneyMax;
}, },
getServerGrowth: (ctx) => (_host) => { getServerGrowth: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${server.serverGrowth} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${server.serverGrowth} for '${server.hostname}'`);
return server.serverGrowth; return server.serverGrowth;
}, },
getServerNumPortsRequired: (ctx) => (_host) => { getServerNumPortsRequired: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const server = helpers.getNormalServer(ctx, _host);
const server = helpers.getNormalServer(ctx, host);
helpers.log(ctx, () => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`); helpers.log(ctx, () => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`);
return server.numOpenPortsRequired; return server.numOpenPortsRequired;
}, },
getServerMaxRam: (ctx) => (_host) => { getServerMaxRam: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return 0;
helpers.log(ctx, () => `returned ${formatRam(server.maxRam)}`); helpers.log(ctx, () => `returned ${formatRam(server.maxRam)}`);
return server.maxRam; return server.maxRam;
}, },
getServerUsedRam: (ctx) => (_host) => { getServerUsedRam: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return 0;
helpers.log(ctx, () => `returned ${formatRam(server.ramUsed)}`); helpers.log(ctx, () => `returned ${formatRam(server.ramUsed)}`);
return server.ramUsed; return server.ramUsed;
}, },
dnsLookup: (ctx) => (_host) => { dnsLookup: (ctx) => (_host?) => {
const host = helpers.string(ctx, "host", _host); const [server, host] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return "";
return isIPAddress(host) ? server.hostname : server.ip; return isIPAddress(host) ? server.hostname : server.ip;
}, },
serverExists: (ctx) => (_host) => { serverExists: (ctx) => (_host) => {
@@ -1097,10 +1058,10 @@ export const ns: InternalAPI<NSFull> = {
const server = GetServer(host); const server = GetServer(host);
return server !== null && (server.serversOnNetwork.length > 0 || server instanceof DarknetServer); return server !== null && (server.serversOnNetwork.length > 0 || server instanceof DarknetServer);
}, },
fileExists: (ctx) => (_filename, _host) => { fileExists: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return false;
const path = resolveFilePath(filename, ctx.workerScript.name); const path = resolveFilePath(filename, ctx.workerScript.name);
if (!path) return false; if (!path) return false;
if (hasScriptExtension(path)) return server.scripts.has(path); if (hasScriptExtension(path)) return server.scripts.has(path);
@@ -1113,7 +1074,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
isRunning: isRunning:
(ctx) => (ctx) =>
(fn, host, ...scriptArgs) => { (fn, host?, ...scriptArgs) => {
const ident = helpers.scriptIdentifier(ctx, fn, host, scriptArgs); const ident = helpers.scriptIdentifier(ctx, fn, host, scriptArgs);
return helpers.getRunningScript(ctx, ident) !== null; return helpers.getRunningScript(ctx, ident) !== null;
}, },
@@ -1126,7 +1087,7 @@ export const ns: InternalAPI<NSFull> = {
const data = helpers.string(ctx, "data", _data ?? ""); const data = helpers.string(ctx, "data", _data ?? "");
const mode = helpers.string(ctx, "mode", _mode ?? "a"); const mode = helpers.string(ctx, "mode", _mode ?? "a");
const server = helpers.getServer(ctx, ctx.workerScript.hostname); const server = ctx.workerScript.getServer();
if (hasScriptExtension(filepath)) { if (hasScriptExtension(filepath)) {
if (mode === "w") { if (mode === "w") {
@@ -1217,10 +1178,10 @@ export const ns: InternalAPI<NSFull> = {
const portHandle = helpers.portHandle(ctx, _portNumber); const portHandle = helpers.portHandle(ctx, _portNumber);
return portHandle; return portHandle;
}, },
rm: (ctx) => (_fn, _host) => { rm: (ctx) => (_fn, _host?) => {
const filepath = helpers.filePath(ctx, "fn", _fn); const filepath = helpers.filePath(ctx, "fn", _fn);
const host = helpers.string(ctx, "host", _host ?? ctx.workerScript.hostname); const [s] = helpers.getServer(ctx, _host);
const s = helpers.getServer(ctx, host); if (!s) return false;
if (!filepath) { if (!filepath) {
helpers.log(ctx, () => `Error while parsing filepath ${filepath}`); helpers.log(ctx, () => `Error while parsing filepath ${filepath}`);
return false; return false;
@@ -1233,16 +1194,16 @@ export const ns: InternalAPI<NSFull> = {
return status.res; return status.res;
}, },
scriptRunning: (ctx) => (_scriptname, _host) => { scriptRunning: (ctx) => (_scriptname, _host?) => {
const scriptname = helpers.scriptPath(ctx, "scriptname", _scriptname); const scriptname = helpers.scriptPath(ctx, "scriptname", _scriptname);
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return false;
return server.isRunning(scriptname); return server.isRunning(scriptname);
}, },
scriptKill: (ctx) => (_scriptname, _host) => { scriptKill: (ctx) => (_scriptname, _host?) => {
const path = helpers.scriptPath(ctx, "scriptname", _scriptname); const path = helpers.scriptPath(ctx, "scriptname", _scriptname);
const host = helpers.string(ctx, "host", _host); const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return false;
let suc = false; let suc = false;
const pattern = matchScriptPathExact(escapeRegExp(path)); const pattern = matchScriptPathExact(escapeRegExp(path));
@@ -1256,10 +1217,10 @@ export const ns: InternalAPI<NSFull> = {
return suc; return suc;
}, },
getScriptName: (ctx) => () => ctx.workerScript.name, getScriptName: (ctx) => () => ctx.workerScript.name,
getScriptRam: (ctx) => (_scriptname, _host) => { getScriptRam: (ctx) => (_scriptname, _host?) => {
const path = helpers.scriptPath(ctx, "scriptname", _scriptname); const path = helpers.scriptPath(ctx, "scriptname", _scriptname);
const host = helpers.string(ctx, "hostname", _host ?? ctx.workerScript.hostname); const [server, host] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) return 0;
const script = server.scripts.get(path); const script = server.scripts.get(path);
if (!script) return 0; if (!script) return 0;
const ramUsage = script.getRamUsage(server.scripts); const ramUsage = script.getRamUsage(server.scripts);
@@ -1271,7 +1232,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
getRunningScript: getRunningScript:
(ctx) => (ctx) =>
(fn, host, ...args) => { (fn, host?, ...args) => {
const ident = helpers.scriptIdentifier(ctx, fn, host, args); const ident = helpers.scriptIdentifier(ctx, fn, host, args);
const runningScript = helpers.getRunningScript(ctx, ident); const runningScript = helpers.getRunningScript(ctx, ident);
if (runningScript === null) return null; if (runningScript === null) return null;
@@ -1304,27 +1265,18 @@ export const ns: InternalAPI<NSFull> = {
rs.ramUsage = newRam; rs.ramUsage = newRam;
return rs.ramUsage; return rs.ramUsage;
}, },
getHackTime: getHackTime: (ctx) => (_host?) => {
(ctx) => const server = helpers.getNormalServer(ctx, _host);
(_host = ctx.workerScript.hostname) => {
const host = helpers.string(ctx, "hostname", _host);
const server = helpers.getNormalServer(ctx, host);
return calculateHackingTime(server, Player) * 1000; return calculateHackingTime(server, Player) * 1000;
}, },
getGrowTime: getGrowTime: (ctx) => (_host?) => {
(ctx) => const server = helpers.getNormalServer(ctx, _host);
(_host = ctx.workerScript.hostname) => {
const host = helpers.string(ctx, "host", _host);
const server = helpers.getNormalServer(ctx, host);
return calculateGrowTime(server, Player) * 1000; return calculateGrowTime(server, Player) * 1000;
}, },
getWeakenTime: getWeakenTime: (ctx) => (_host) => {
(ctx) => const server = helpers.getNormalServer(ctx, _host);
(_host = ctx.workerScript.hostname) => {
const host = helpers.string(ctx, "hostname", _host);
const server = helpers.getNormalServer(ctx, host);
return calculateWeakenTime(server, Player) * 1000; return calculateWeakenTime(server, Player) * 1000;
}, },
@@ -1343,7 +1295,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
getScriptIncome: getScriptIncome:
(ctx) => (ctx) =>
(fn, host, ...args) => { (fn, host?, ...args) => {
const ident = helpers.scriptIdentifier(ctx, fn, host, args); const ident = helpers.scriptIdentifier(ctx, fn, host, args);
const runningScript = helpers.getRunningScript(ctx, ident); const runningScript = helpers.getRunningScript(ctx, ident);
if (runningScript == null) { if (runningScript == null) {
@@ -1361,7 +1313,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
getScriptExpGain: getScriptExpGain:
(ctx) => (ctx) =>
(fn, host, ...args) => { (fn, host?, ...args) => {
const ident = helpers.scriptIdentifier(ctx, fn, host, args); const ident = helpers.scriptIdentifier(ctx, fn, host, args);
const runningScript = helpers.getRunningScript(ctx, ident); const runningScript = helpers.getRunningScript(ctx, ident);
if (runningScript == null) { if (runningScript == null) {
@@ -1425,15 +1377,17 @@ export const ns: InternalAPI<NSFull> = {
}); });
}); });
}, },
wget: (ctx) => async (_url, _target, _host) => { wget: (ctx) => async (_url, _target, _host?) => {
const url = helpers.string(ctx, "url", _url); const url = helpers.string(ctx, "url", _url);
const target = helpers.filePath(ctx, "target", _target); const target = helpers.filePath(ctx, "target", _target);
const host = _host ? helpers.string(ctx, "hostname", _host) : ctx.workerScript.hostname; const [server, host] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host);
if (!target || (!hasTextExtension(target) && !hasScriptExtension(target))) { if (!target || (!hasTextExtension(target) && !hasScriptExtension(target))) {
helpers.log(ctx, () => `Invalid target file: '${target}'. Must be a script or text file.`); helpers.log(ctx, () => `Invalid target file: '${target}'. Must be a script or text file.`);
return false; return false;
} }
if (!server) {
return false;
}
let response: Response; let response: Response;
try { try {
response = await fetch(url); response = await fetch(url);
@@ -1495,8 +1449,10 @@ export const ns: InternalAPI<NSFull> = {
ctx.workerScript.atExit.set(id, callback); ctx.workerScript.atExit.set(id, callback);
}, },
mv: (ctx) => (_host, _source, _destination) => { mv: (ctx) => (_host, _source, _destination) => {
const host = helpers.string(ctx, "host", _host); const [server, host] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (!server) {
return;
}
const sourcePath = helpers.filePath(ctx, "source", _source); const sourcePath = helpers.filePath(ctx, "source", _source);
const destinationPath = helpers.filePath(ctx, "destination", _destination); const destinationPath = helpers.filePath(ctx, "destination", _destination);
@@ -1559,7 +1515,7 @@ export const ns: InternalAPI<NSFull> = {
}, },
dynamicImport: (ctx) => async (value) => { dynamicImport: (ctx) => async (value) => {
const path = helpers.scriptPath(ctx, "path", value); const path = helpers.scriptPath(ctx, "path", value);
const server = helpers.getServer(ctx, ctx.workerScript.hostname); const server = ctx.workerScript.getServer();
const script = server.getContentFile(path); const script = server.getContentFile(path);
if (!script) throw helpers.errorMessage(ctx, `Script was not found\nPath: ${path}`); if (!script) throw helpers.errorMessage(ctx, `Script was not found\nPath: ${path}`);
+3 -3
View File
@@ -10,7 +10,7 @@ import {
renameCloudServer, renameCloudServer,
upgradeCloudServer, upgradeCloudServer,
} from "../Server/ServerPurchases"; } from "../Server/ServerPurchases";
import { DeleteServer, AddToAllServers, createUniqueRandomIp } from "../Server/AllServers"; import { DeleteServer, AddToAllServers, createUniqueRandomIp, GetServerOrThrow } from "../Server/AllServers";
import { safelyCreateUniqueServer } from "../Server/ServerHelpers"; import { safelyCreateUniqueServer } from "../Server/ServerHelpers";
import { formatMoney } from "../ui/formatNumber"; import { formatMoney } from "../ui/formatNumber";
import { isIPAddress } from "../Types/strings"; import { isIPAddress } from "../Types/strings";
@@ -195,12 +195,12 @@ export function NetscriptCloud(): InternalAPI<Cloud> {
return false; return false;
}, },
getServerNames: getServerNames:
(ctx) => () =>
(_returnOpts): string[] => { (_returnOpts): string[] => {
const returnOpts = helpers.hostReturnOptions(_returnOpts); const returnOpts = helpers.hostReturnOptions(_returnOpts);
const res: string[] = []; const res: string[] = [];
for (const hostname of Player.purchasedServers) { for (const hostname of Player.purchasedServers) {
const server = helpers.getServer(ctx, hostname); const server = GetServerOrThrow(hostname);
const id = helpers.returnServerID(server, returnOpts); const id = helpers.returnServerID(server, returnOpts);
res.push(id); res.push(id);
} }
+20 -22
View File
@@ -10,14 +10,18 @@ import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getEnumHelper } from "../utils/EnumHelper"; import { getEnumHelper } from "../utils/EnumHelper";
export function NetscriptCodingContract(): InternalAPI<ICodingContract> { export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
const getCodingContract = function (ctx: NetscriptContext, hostname: string, filename: string): CodingContract { const getCodingContract = function (
const server = helpers.getServer(ctx, hostname); ctx: NetscriptContext,
const contract = server.getContract(filename); _host: unknown,
if (contract == null) { filename: string,
throw helpers.errorMessage(ctx, `Cannot find contract '${filename}' on server '${hostname}'`); ): [CodingContract, BaseServer] {
const [server, host] = helpers.getServer(ctx, _host);
const contract = server?.getContract(filename);
if (server == null || contract == null) {
throw helpers.errorMessage(ctx, `Cannot find contract '${filename}' on server '${host}'`);
} }
return contract; return [contract, server];
}; };
function attemptContract( function attemptContract(
@@ -81,28 +85,22 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
return { return {
attempt: (ctx) => (answer, _filename, _host?) => { attempt: (ctx) => (answer, _filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract, server] = getCodingContract(ctx, _host, filename);
const contract = getCodingContract(ctx, host, filename);
const server = helpers.getServer(ctx, host);
return attemptContract(ctx, server, contract, answer); return attemptContract(ctx, server, contract, answer);
}, },
getContractType: (ctx) => (_filename, _host?) => { getContractType: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract] = getCodingContract(ctx, _host, filename);
const contract = getCodingContract(ctx, host, filename);
return contract.getType(); return contract.getType();
}, },
getData: (ctx) => (_filename, _host?) => { getData: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract] = getCodingContract(ctx, _host, filename);
const contract = getCodingContract(ctx, host, filename);
return structuredClone(contract.getData()); return structuredClone(contract.getData());
}, },
getContract: (ctx) => (_filename, _host?) => { getContract: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract, server] = getCodingContract(ctx, _host, filename);
const server = helpers.getServer(ctx, host);
const contract = getCodingContract(ctx, host, filename);
// asserting type here is required, since it is not feasible to properly type getData // asserting type here is required, since it is not feasible to properly type getData
return { return {
type: contract.type, type: contract.type,
@@ -121,20 +119,20 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
}, },
getDescription: (ctx) => (_filename, _host?) => { getDescription: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract] = getCodingContract(ctx, _host, filename);
const contract = getCodingContract(ctx, host, filename);
return contract.getDescription(); return contract.getDescription();
}, },
getNumTriesRemaining: (ctx) => (_filename, _host?) => { getNumTriesRemaining: (ctx) => (_filename, _host?) => {
const filename = helpers.string(ctx, "filename", _filename); const filename = helpers.string(ctx, "filename", _filename);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [contract] = getCodingContract(ctx, _host, filename);
const contract = getCodingContract(ctx, host, filename);
return contract.getMaxNumTries() - contract.tries; return contract.getMaxNumTries() - contract.tries;
}, },
createDummyContract: (ctx) => (_type, _host?) => { createDummyContract: (ctx) => (_type, _host?) => {
const type = getEnumHelper("CodingContractName").nsGetMember(ctx, _type); const type = getEnumHelper("CodingContractName").nsGetMember(ctx, _type);
const host = _host ? helpers.string(ctx, "host", _host) : ctx.workerScript.hostname; const [server] = helpers.getServer(ctx, _host);
const server = helpers.getServer(ctx, host); if (server == null) {
return null;
}
return generateDummyContract(type, server); return generateDummyContract(type, server);
}, },
getContractTypes: () => () => Object.values(CodingContractName), getContractTypes: () => () => Object.values(CodingContractName),
+3 -8
View File
@@ -470,16 +470,11 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const server = Player.getCurrentServer(); const server = Player.getCurrentServer();
cat([filename], server); cat([filename], server);
}, },
connect: (ctx) => (_host) => { connect: (ctx) => (_host?) => {
helpers.checkSingularityAccess(ctx); helpers.checkSingularityAccess(ctx);
const host = helpers.string(ctx, "host", _host); const [target, host] = helpers.getServer(ctx, _host);
if (!host) {
throw helpers.errorMessage(ctx, `Invalid server: '${host}'`);
}
const target = GetServer(host);
if (target == null) { if (target == null) {
throw helpers.errorMessage(ctx, `Invalid server: '${host}'`); return false;
} }
// Adjacent servers // Adjacent servers
+1 -1
View File
@@ -44,7 +44,7 @@ export function NetscriptStanek(): InternalAPI<IStanek> {
); );
} }
//Charge the fragment //Charge the fragment
const cores = helpers.getServer(ctx, ctx.workerScript.hostname).cpuCores; const cores = ctx.workerScript.getServer().cpuCores;
const coreBonus = getCoreBonus(cores); const coreBonus = getCoreBonus(cores);
const inBonus = staneksGift.inBonus(); const inBonus = staneksGift.inBonus();
const time = inBonus ? 200 : 1000; const time = inBonus ? 200 : 1000;
+67 -65
View File
@@ -4067,7 +4067,8 @@ export interface CodingContract {
* *
* Generate a dummy contract on the current server with no reward. Used to test various algorithms. * Generate a dummy contract on the current server with no reward. Used to test various algorithms.
* *
* This function will return null and not generate a contract if the randomized contract name is the same as another contract's name. * This function will return null and not generate a contract if the randomized contract name is the same as another
* contract's name or the host is offline.
* *
* @param type - Type of contract to generate * @param type - Type of contract to generate
* @param host - Hostname/IP of the server containing the contract. Optional. Defaults to the server the calling script is running on. * @param host - Hostname/IP of the server containing the contract. Optional. Defaults to the server the calling script is running on.
@@ -7060,11 +7061,11 @@ export interface NS {
* ```js * ```js
* let earnedMoney = await ns.hack("foodnstuff"); * let earnedMoney = await ns.hack("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server to hack. * @param host - Hostname/IP of the target server to hack. Optional. Defaults to current server if not provided.
* @param opts - Optional parameters for configuring function behavior. * @param opts - Optional parameters for configuring function behavior.
* @returns A promise that resolves to the amount of money stolen (which is zero if the hack is unsuccessful). * @returns A promise that resolves to the amount of money stolen (which is zero if the hack is unsuccessful).
*/ */
hack(host: string, opts?: BasicHGWOptions): Promise<number>; hack(host?: string, opts?: BasicHGWOptions): Promise<number>;
/** /**
* Spoof money in a server's bank account, increasing the amount available. * Spoof money in a server's bank account, increasing the amount available.
@@ -7106,11 +7107,11 @@ export interface NS {
* let currentMoney = ns.getServerMoneyAvailable("n00dles"); * let currentMoney = ns.getServerMoneyAvailable("n00dles");
* currentMoney *= await ns.grow("n00dles"); * currentMoney *= await ns.grow("n00dles");
* ``` * ```
* @param host - Hostname/IP of the target server to grow. * @param host - Hostname/IP of the target server to grow. Optional. Defaults to current server if not provided.
* @param opts - Optional parameters for configuring function behavior. * @param opts - Optional parameters for configuring function behavior.
* @returns The total effective multiplier that was applied to the server's money (after both additive and multiplicative growth). * @returns The total effective multiplier that was applied to the server's money (after both additive and multiplicative growth).
*/ */
grow(host: string, opts?: BasicHGWOptions): Promise<number>; grow(host?: string, opts?: BasicHGWOptions): Promise<number>;
/** /**
* Reduce a server's security level. * Reduce a server's security level.
@@ -7133,11 +7134,11 @@ export interface NS {
* let currentSecurity = ns.getServerSecurityLevel("foodnstuff"); * let currentSecurity = ns.getServerSecurityLevel("foodnstuff");
* currentSecurity -= await ns.weaken("foodnstuff"); * currentSecurity -= await ns.weaken("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server to weaken. * @param host - Hostname/IP of the target server to weaken. Optional. Defaults to current server if not provided.
* @param opts - Optional parameters for configuring function behavior. * @param opts - Optional parameters for configuring function behavior.
* @returns A promise that resolves to the value by which security was reduced. * @returns A promise that resolves to the value by which security was reduced.
*/ */
weaken(host: string, opts?: BasicHGWOptions): Promise<number>; weaken(host?: string, opts?: BasicHGWOptions): Promise<number>;
/** /**
* Predict the effect of weaken. * Predict the effect of weaken.
@@ -7173,7 +7174,7 @@ export interface NS {
* ns.run("noodleHack.js", Math.floor(hackThreads)); * ns.run("noodleHack.js", Math.floor(hackThreads));
* *
* ``` * ```
* @param host - Hostname of the target server to analyze. * @param host - Hostname/IP of the target server to analyze.
* @param hackAmount - Amount of money you want to hack from the server. * @param hackAmount - Amount of money you want to hack from the server.
* @returns The number of threads needed to hack the server for hackAmount money. * @returns The number of threads needed to hack the server for hackAmount money.
*/ */
@@ -7206,9 +7207,10 @@ export interface NS {
* RAM cost: 1 GB * RAM cost: 1 GB
* *
* Returns the security increase that would occur if a hack with this many threads happened. * Returns the security increase that would occur if a hack with this many threads happened.
* The number of threads is limited to the number needed to hack the server's maximum amount of money.
* *
* @param threads - Amount of threads that will be used. * @param threads - Amount of threads that will be used.
* @param host - Hostname/IP of the target server. The number of threads is limited to the number needed to hack the server's maximum amount of money. * @param host - Hostname/IP of the target server. Optional. If unspecified, the threads are not capped.
* @returns The security increase. * @returns The security increase.
*/ */
hackAnalyzeSecurity(threads: number, host?: string): number; hackAnalyzeSecurity(threads: number, host?: string): number;
@@ -7225,10 +7227,10 @@ export interface NS {
* Like other basic hacking analysis functions, this calculation uses the current status of the player and server. * Like other basic hacking analysis functions, this calculation uses the current status of the player and server.
* To calculate using hypothetical server or player status, obtain access to the Formulas API and use {@link HackingFormulas.hackChance | formulas.hacking.hackChance}. * To calculate using hypothetical server or player status, obtain access to the Formulas API and use {@link HackingFormulas.hackChance | formulas.hacking.hackChance}.
* *
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns The chance you have of successfully hacking the target server. * @returns The chance you have of successfully hacking the target server.
*/ */
hackAnalyzeChance(host: string): number; hackAnalyzeChance(host?: string): number;
/** /**
* Calculate the number of grow threads needed for a given multiplicative growth factor. * Calculate the number of grow threads needed for a given multiplicative growth factor.
@@ -7659,10 +7661,10 @@ export interface NS {
* ```js * ```js
* ns.nuke("foodnstuff"); * ns.nuke("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
nuke(host: string): boolean; nuke(host?: string): boolean;
/** /**
* Runs BruteSSH.exe on a server. * Runs BruteSSH.exe on a server.
@@ -7675,10 +7677,10 @@ export interface NS {
* ```js * ```js
* ns.brutessh("foodnstuff"); * ns.brutessh("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
brutessh(host: string): boolean; brutessh(host?: string): boolean;
/** /**
* Runs FTPCrack.exe on a server. * Runs FTPCrack.exe on a server.
@@ -7691,10 +7693,10 @@ export interface NS {
* ```js * ```js
* ns.ftpcrack("foodnstuff"); * ns.ftpcrack("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
ftpcrack(host: string): boolean; ftpcrack(host?: string): boolean;
/** /**
* Runs relaySMTP.exe on a server. * Runs relaySMTP.exe on a server.
@@ -7707,10 +7709,10 @@ export interface NS {
* ```js * ```js
* ns.relaysmtp("foodnstuff"); * ns.relaysmtp("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
relaysmtp(host: string): boolean; relaysmtp(host?: string): boolean;
/** /**
* Runs HTTPWorm.exe on a server. * Runs HTTPWorm.exe on a server.
@@ -7723,10 +7725,10 @@ export interface NS {
* ```js * ```js
* ns.httpworm("foodnstuff"); * ns.httpworm("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
httpworm(host: string): boolean; httpworm(host?: string): boolean;
/** /**
* Runs SQLInject.exe on a server. * Runs SQLInject.exe on a server.
@@ -7739,10 +7741,10 @@ export interface NS {
* ```js * ```js
* ns.sqlinject("foodnstuff"); * ns.sqlinject("foodnstuff");
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the player runs the program successfully, and false otherwise. * @returns True if the player runs the program successfully, and false otherwise.
*/ */
sqlinject(host: string): boolean; sqlinject(host?: string): boolean;
/** /**
* Start another script on the current server. * Start another script on the current server.
@@ -7815,7 +7817,7 @@ export interface NS {
* ns.exec("foo.js", "foodnstuff", 5, 1, "test"); * ns.exec("foo.js", "foodnstuff", 5, 1, "test");
* ``` * ```
* @param script - Filename of script to execute. This file must already exist on the target server. * @param script - Filename of script to execute. This file must already exist on the target server.
* @param host - Hostname/IP of the `target server` on which to execute the script. * @param host - Hostname/IP of the target server on which to execute the script.
* @param threadOrOptions - Either an integer number of threads for new script, or a {@link RunOptions} object. Threads defaults to 1. * @param threadOrOptions - Either an integer number of threads for new script, or a {@link RunOptions} object. Threads defaults to 1.
* @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument threadOrOptions must be filled in with a value. * @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument threadOrOptions must be filled in with a value.
* @returns Returns the PID of a successfully started script, and 0 otherwise. * @returns Returns the PID of a successfully started script, and 0 otherwise.
@@ -7914,7 +7916,7 @@ export interface NS {
* true if there are any scripts running on the target server. * true if there are any scripts running on the target server.
* If no host is defined, it will kill all scripts, where the script is running. * If no host is defined, it will kill all scripts, where the script is running.
* *
* @param host - Hostname/IP of the server on which to kill all scripts. * @param host - Hostname/IP of the server on which to kill all scripts. Optional. Defaults to current server if not provided.
* @param safetyGuard - Skips the script that calls this function * @param safetyGuard - Skips the script that calls this function
* @returns True if any scripts were killed, and false otherwise. * @returns True if any scripts were killed, and false otherwise.
*/ */
@@ -8007,10 +8009,10 @@ export interface NS {
* ns.nuke("foodnstuff"); * ns.nuke("foodnstuff");
* } * }
* ``` * ```
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if player has root access to the specified target server, and false otherwise. * @returns True if player has root access to the specified target server, and false otherwise.
*/ */
hasRootAccess(host: string): boolean; hasRootAccess(host?: string): boolean;
/** /**
* Returns a string with the hostname of the server that the script is running on. * Returns a string with the hostname of the server that the script is running on.
@@ -8104,10 +8106,10 @@ export interface NS {
* ns.getServerMoneyAvailable("foodnstuff"); * ns.getServerMoneyAvailable("foodnstuff");
* ns.getServerMoneyAvailable("home"); // Returns player's money * ns.getServerMoneyAvailable("home"); // Returns player's money
* ``` * ```
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Amount of money available on the server. * @returns Amount of money available on the server.
*/ */
getServerMoneyAvailable(host: string): number; getServerMoneyAvailable(host?: string): number;
/** /**
* Get the maximum money available on a server. * Get the maximum money available on a server.
@@ -8116,10 +8118,10 @@ export interface NS {
* *
* Returns the maximum amount of money that can be available on a server. * Returns the maximum amount of money that can be available on a server.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Maximum amount of money available on the server. * @returns Maximum amount of money available on the server.
*/ */
getServerMaxMoney(host: string): number; getServerMaxMoney(host?: string): number;
/** /**
* Get a server growth parameter. * Get a server growth parameter.
@@ -8133,10 +8135,10 @@ export interface NS {
* grow function. A higher growth parameter will result in a * grow function. A higher growth parameter will result in a
* higher percentage increase from grow. * higher percentage increase from grow.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Parameter that affects the percentage by which the servers money is increased when using the grow function. * @returns Parameter that affects the percentage by which the servers money is increased when using the grow function.
*/ */
getServerGrowth(host: string): number; getServerGrowth(host?: string): number;
/** /**
* Get server security level. * Get server security level.
@@ -8147,19 +8149,19 @@ export interface NS {
* level is denoted by a number, typically between 1 and 100 * level is denoted by a number, typically between 1 and 100
* (but it can go above 100). * (but it can go above 100).
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Security level of the target server. * @returns Security level of the target server.
*/ */
getServerSecurityLevel(host: string): number; getServerSecurityLevel(host?: string): number;
/** /**
* Returns the minimum security level of the target server. * Returns the minimum security level of the target server.
* *
* @remarks RAM cost: 0.1 GB * @remarks RAM cost: 0.1 GB
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Minimum security level of the target server. * @returns Minimum security level of the target server.
*/ */
getServerMinSecurityLevel(host: string): number; getServerMinSecurityLevel(host?: string): number;
/** /**
* Get the base security level of a server. * Get the base security level of a server.
@@ -8168,64 +8170,64 @@ export interface NS {
* Returns the base security level of the target server. * Returns the base security level of the target server.
* For the server's actual security level, use {@link NS.getServerSecurityLevel | ns.getServerSecurityLevel}. * For the server's actual security level, use {@link NS.getServerSecurityLevel | ns.getServerSecurityLevel}.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Base security level of the target server. * @returns Base security level of the target server.
*/ */
getServerBaseSecurityLevel(host: string): number; getServerBaseSecurityLevel(host?: string): number;
/** /**
* Get the maximum amount of RAM on a server. * Get the maximum amount of RAM on a server.
* @remarks * @remarks
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns The maximum amount of RAM (GB) a server can have. * @returns The maximum amount of RAM (GB) a server can have.
*/ */
getServerMaxRam(host: string): number; getServerMaxRam(host?: string): number;
/** /**
* Get the used RAM on a server. * Get the used RAM on a server.
* @remarks * @remarks
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* @param host - Hostname/IP of the target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns The amount of used RAM (GB) on the specified server. * @returns The amount of used RAM (GB) on the specified server.
*/ */
getServerUsedRam(host: string): number; getServerUsedRam(host?: string): number;
/** /**
* Returns the required hacking level of the target server. * Returns the required hacking level of the target server.
* *
* @remarks RAM cost: 0.1 GB * @remarks RAM cost: 0.1 GB
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns The required hacking level of the target server. * @returns The required hacking level of the target server.
*/ */
getServerRequiredHackingLevel(host: string): number; getServerRequiredHackingLevel(host?: string): number;
/** /**
* Returns the number of open ports required to successfully run NUKE.exe on the specified server. * Returns the number of open ports required to successfully run NUKE.exe on the specified server.
* *
* @remarks RAM cost: 0.1 GB * @remarks RAM cost: 0.1 GB
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns The number of open ports required to successfully run NUKE.exe on the specified server. * @returns The number of open ports required to successfully run NUKE.exe on the specified server.
*/ */
getServerNumPortsRequired(host: string): number; getServerNumPortsRequired(host?: string): number;
/** /**
* Given a hostname, returns its IP address; or given an IP address, returns its hostname. * Given a hostname, returns its IP address; or given an IP address, returns its hostname.
* *
* @remarks RAM cost: 0.05 GB * @remarks RAM cost: 0.05 GB
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
*/ */
dnsLookup(host: string): string; dnsLookup(host?: string): string;
/** /**
* Returns a boolean denoting whether or not the specified server exists. * Returns a boolean denoting whether or not the specified server exists.
* *
* @remarks RAM cost: 0.1 GB * @remarks RAM cost: 0.1 GB
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the specified server exists, and false otherwise. * @returns True if the specified server exists, and false otherwise.
*/ */
serverExists(host: string): boolean; serverExists(host?: string): boolean;
/** /**
* Check if a file exists. * Check if a file exists.
@@ -8515,10 +8517,10 @@ export interface NS {
* ns.scriptRunning("foo.js", ns.getHostname()); * ns.scriptRunning("foo.js", ns.getHostname());
* ``` * ```
* @param script - Filename of script to check. This is case-sensitive. * @param script - Filename of script to check. This is case-sensitive.
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if the specified script is running, and false otherwise. * @returns True if the specified script is running, and false otherwise.
*/ */
scriptRunning(script: string, host: string): boolean; scriptRunning(script: string, host?: string): boolean;
/** /**
* Kill all scripts with a filename. * Kill all scripts with a filename.
@@ -8529,10 +8531,10 @@ export interface NS {
* regardless of arguments. * regardless of arguments.
* *
* @param script - Filename of script to kill. This is case-sensitive. * @param script - Filename of script to kill. This is case-sensitive.
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns True if one or more scripts were successfully killed, and false if none were. * @returns True if one or more scripts were successfully killed, and false if none were.
*/ */
scriptKill(script: string, host: string): boolean; scriptKill(script: string, host?: string): boolean;
/** /**
* Returns the current script name. * Returns the current script name.
@@ -8565,10 +8567,10 @@ export interface NS {
* Returns the amount of time in milliseconds it takes to execute the {@link NS.hack | hack} Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the {@link NS.hack | hack} Netscript function on the target server.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Returns the amount of time in milliseconds it takes to execute the {@link NS.hack | hack} Netscript function. * @returns Returns the amount of time in milliseconds it takes to execute the {@link NS.hack | hack} Netscript function.
*/ */
getHackTime(host: string): number; getHackTime(host?: string): number;
/** /**
* Get the execution time of a grow() call. * Get the execution time of a grow() call.
@@ -8578,10 +8580,10 @@ export interface NS {
* Returns the amount of time in milliseconds it takes to execute the grow Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the grow Netscript function on the target server.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Returns the amount of time in milliseconds it takes to execute the grow Netscript function. * @returns Returns the amount of time in milliseconds it takes to execute the grow Netscript function.
*/ */
getGrowTime(host: string): number; getGrowTime(host?: string): number;
/** /**
* Get the execution time of a weaken() call. * Get the execution time of a weaken() call.
@@ -8591,10 +8593,10 @@ export interface NS {
* Returns the amount of time in milliseconds it takes to execute the {@link NS.weaken | weaken} Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the {@link NS.weaken | weaken} Netscript function on the target server.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Hostname/IP of target server. * @param host - Hostname/IP of the target server. Optional. Defaults to current server if not provided.
* @returns Returns the amount of time in milliseconds it takes to execute the {@link NS.weaken | weaken} Netscript function. * @returns Returns the amount of time in milliseconds it takes to execute the {@link NS.weaken | weaken} Netscript function.
*/ */
getWeakenTime(host: string): number; getWeakenTime(host?: string): number;
/** /**
* Get the income of all scripts. * Get the income of all scripts.
@@ -8622,11 +8624,11 @@ export interface NS {
* those same arguments in the same order in this function call. * those same arguments in the same order in this function call.
* *
* @param script - Filename of script. * @param script - Filename of script.
* @param host - Hostname/IP of the server on which script is running. * @param host - Hostname/IP of the server on which the script is running. Optional. Defaults to current server if not provided.
* @param args - Arguments that the script is running with. * @param args - Arguments that the script is running with.
* @returns Amount of income the specified script generates while online. * @returns Amount of income the specified script generates while online.
*/ */
getScriptIncome(script: string, host: string, ...args: ScriptArg[]): number; getScriptIncome(script: string, host?: string, ...args: ScriptArg[]): number;
/** /**
* Get the exp gain of all scripts. * Get the exp gain of all scripts.
@@ -8650,11 +8652,11 @@ export interface NS {
* scripts by running the function with no arguments. * scripts by running the function with no arguments.
* *
* @param script - Filename of script. * @param script - Filename of script.
* @param host - Hostname/IP of the server on which script is running. * @param host - Hostname/IP of the server on which the script is running. Optional. Defaults to current server if not provided.
* @param args - Arguments that the script is running with. * @param args - Arguments that the script is running with.
* @returns Amount of hacking experience the specified script generates while online. * @returns Amount of hacking experience the specified script generates while online.
*/ */
getScriptExpGain(script: string, host: string, ...args: ScriptArg[]): number; getScriptExpGain(script: string, host?: string, ...args: ScriptArg[]): number;
/** /**
* Format a string. * Format a string.
+3 -3
View File
@@ -534,10 +534,10 @@ describe("Password Tests", () => {
expect(isNumber(+server.password)).toBe(true); expect(isNumber(+server.password)).toBe(true);
expect(isNumber(+server.passwordHintData)).toBe(true); expect(isNumber(+server.passwordHintData)).toBe(true);
DarknetState.serverState[server.hostname] = { DarknetState.serverState.set(server.hostname, {
serverLogs: [], serverLogs: [],
authenticatedPIDs: [], authenticatedPIDs: [],
}; });
const nonDivisibleResult = getAuthResult(server, `${server.password + 1}`, 1); const nonDivisibleResult = getAuthResult(server, `${server.password + 1}`, 1);
expect(nonDivisibleResult.response.code).toBe(ResponseCodeEnum.AuthFailure); expect(nonDivisibleResult.response.code).toBe(ResponseCodeEnum.AuthFailure);
expect(nonDivisibleResult.response.message).toContain("not divisible"); expect(nonDivisibleResult.response.message).toContain("not divisible");
@@ -741,7 +741,7 @@ describe("Darknet server name generator", () => {
} }
}); });
test("generateDarknetServerName", () => { test("generateDarknetServerName", () => {
DarknetState.offlineServers = []; DarknetState.offlineServers = new Set();
for (let i = 0; i < 1000; ++i) { for (let i = 0; i < 1000; ++i) {
validatePath(generateDarknetServerName()); validatePath(generateDarknetServerName());
} }
+52 -35
View File
@@ -1,5 +1,6 @@
import { AugmentationName, CompletedProgramName } from "@enums"; import { AugmentationName, CompletedProgramName } from "@enums";
import { Player } from "@player"; import { Player } from "@player";
import type { DarknetResult } from "@nsdefs";
import { PlayerOwnedAugmentation } from "../../../src/Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../../../src/Augmentation/PlayerOwnedAugmentation";
import { addCacheToServer } from "../../../src/DarkNet/effects/cacheFiles"; import { addCacheToServer } from "../../../src/DarkNet/effects/cacheFiles";
import { getDarkscapeNavigator } from "../../../src/DarkNet/effects/effects"; import { getDarkscapeNavigator } from "../../../src/DarkNet/effects/effects";
@@ -35,8 +36,9 @@ import type { Result } from "@nsdefs";
import { assertNonNullish } from "../../../src/utils/TypeAssertion"; import { assertNonNullish } from "../../../src/utils/TypeAssertion";
const hostnameOfNonExistentServer = "fake-server"; const hostnameOfNonExistentServer = "fake-server";
const errorMessageForNonExistentServer = `Server ${hostnameOfNonExistentServer} does not exist.`; const errorMessageForNonExistentServer = `Invalid host: '${hostnameOfNonExistentServer}'`;
const hostnameForOfflineServer = "darknet-offline-server"; const hostnameForOfflineServer = "darknet-offline-server";
const ipForOfflineServer = "0.0.0.0";
fixDoImportIssue(); fixDoImportIssue();
@@ -45,7 +47,7 @@ beforeAll(() => {
initStockMarket(); initStockMarket();
}); });
beforeEach(() => { beforeEach(() => {
DarknetState.offlineServers = []; DarknetState.offlineServers = new Set();
setupBasicTestingEnvironment({ purchasePServer: true, purchaseHacknetServer: true }); setupBasicTestingEnvironment({ purchasePServer: true, purchaseHacknetServer: true });
Player.sourceFiles.set(15, 1); Player.sourceFiles.set(15, 1);
getDarkscapeNavigator(); getDarkscapeNavigator();
@@ -1060,81 +1062,96 @@ describe("Non-darkweb darknet server", () => {
describe("Offline darknet server", () => { describe("Offline darknet server", () => {
beforeEach(() => { beforeEach(() => {
DarknetState.offlineServers.push(hostnameForOfflineServer); DarknetState.offlineServers.add(hostnameForOfflineServer);
DarknetState.offlineServers.add(ipForOfflineServer);
}); });
test("authenticate from home", async () => { async function testIpAndHostname(func: (host: string) => Promise<DarknetResult>) {
const ns = getNsOnHome(); let result = await func(hostnameForOfflineServer);
const result = await ns.dnet.authenticate(hostnameForOfflineServer, "");
expect(result.success).toStrictEqual(false); expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable); expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
result = await func(ipForOfflineServer);
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}
test("authenticate from home", async () => {
const ns = getNsOnHome();
await testIpAndHostname((host) => ns.dnet.authenticate(host, ""));
}); });
test("authenticate itself", async () => { test("authenticate itself", async () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = await ns.dnet.authenticate(hostnameForOfflineServer, ""); await testIpAndHostname((host) => ns.dnet.authenticate(host, ""));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("connectToSession from home", () => { test("connectToSession from home", async () => {
const ns = getNsOnHome(); const ns = getNsOnHome();
const result = ns.dnet.connectToSession(hostnameForOfflineServer, ""); await testIpAndHostname((host) => Promise.resolve(ns.dnet.connectToSession(host, "")));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("heartbleed from home", async () => { test("heartbleed from home", async () => {
const ns = getNsOnHome(); const ns = getNsOnHome();
const result = await ns.dnet.heartbleed(hostnameForOfflineServer); await testIpAndHostname((host) => ns.dnet.heartbleed(host));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("getServer", () => { test("getServer", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const server = ns.getServer(hostnameForOfflineServer); let server = ns.getServer(hostnameForOfflineServer);
if (!("isOnline" in server)) { expect(server).toHaveProperty("isOnline", false);
throw new Error("getServer does not return DarknetServerData"); expect(server.hostname).toBe(hostnameForOfflineServer);
} expect(server.ip).toBe("");
expect(server.isOnline).toStrictEqual(false);
server = ns.getServer(ipForOfflineServer);
expect(server).toHaveProperty("isOnline", false);
expect(server.hostname).toBe("");
expect(server.ip).toBe(ipForOfflineServer);
}); });
test("getServerAuthDetails", () => { test("getServerAuthDetails", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const authDetails = ns.dnet.getServerAuthDetails(hostnameForOfflineServer); let authDetails = ns.dnet.getServerAuthDetails(hostnameForOfflineServer);
expect(authDetails.isOnline).toStrictEqual(false);
authDetails = ns.dnet.getServerAuthDetails(ipForOfflineServer);
expect(authDetails.isOnline).toStrictEqual(false); expect(authDetails.isOnline).toStrictEqual(false);
}); });
test("packetCapture from home", async () => { test("packetCapture from home", async () => {
const ns = getNsOnHome(); const ns = getNsOnHome();
const result = await ns.dnet.packetCapture(hostnameForOfflineServer); await testIpAndHostname((host) => ns.dnet.packetCapture(host));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("induceServerMigration", async () => { test("induceServerMigration", async () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = await ns.dnet.induceServerMigration(hostnameForOfflineServer); await testIpAndHostname((host) => ns.dnet.induceServerMigration(host));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("isDarknetServer", () => { test("isDarknetServer", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = ns.dnet.isDarknetServer(hostnameForOfflineServer); let result = ns.dnet.isDarknetServer(hostnameForOfflineServer);
expect(result).toStrictEqual(false);
result = ns.dnet.isDarknetServer(ipForOfflineServer);
expect(result).toStrictEqual(false); expect(result).toStrictEqual(false);
}); });
test("memoryReallocation", async () => { test("memoryReallocation", async () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = await ns.dnet.memoryReallocation(hostnameForOfflineServer); await testIpAndHostname((host) => ns.dnet.memoryReallocation(host));
expect(result.success).toStrictEqual(false);
expect(result.code).toStrictEqual(ResponseCodeEnum.ServiceUnavailable);
}); });
test("getBlockedRam", () => { test("getBlockedRam", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = ns.dnet.getBlockedRam(hostnameForOfflineServer); let result = ns.dnet.getBlockedRam(hostnameForOfflineServer);
expect(result).toStrictEqual(0);
result = ns.dnet.getBlockedRam(ipForOfflineServer);
expect(result).toStrictEqual(0); expect(result).toStrictEqual(0);
}); });
test("getDepth", () => { test("getDepth", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = ns.dnet.getDepth(hostnameForOfflineServer); let result = ns.dnet.getDepth(hostnameForOfflineServer);
expect(result).toStrictEqual(-1);
result = ns.dnet.getDepth(ipForOfflineServer);
expect(result).toStrictEqual(-1); expect(result).toStrictEqual(-1);
}); });
test("getServerRequiredCharismaLevel", () => { test("getServerRequiredCharismaLevel", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const result = ns.dnet.getServerRequiredCharismaLevel(hostnameForOfflineServer); let result = ns.dnet.getServerRequiredCharismaLevel(hostnameForOfflineServer);
expect(result).toStrictEqual(-1);
result = ns.dnet.getServerRequiredCharismaLevel(ipForOfflineServer);
expect(result).toStrictEqual(-1); expect(result).toStrictEqual(-1);
}); });
}); });