DNET: Rebalance / player feedback (#2533)

This commit is contained in:
Michael Ficocelli
2026-02-28 12:48:03 -07:00
committed by GitHub
parent 15e1ab9af7
commit 3d41c348bc
12 changed files with 51 additions and 23 deletions

View File

@@ -17,7 +17,7 @@ unleashStormSeed(): DarknetResult;
[DarknetResult](./bitburner.darknetresult.md)
A promise that resolves to a [DarknetResult](./bitburner.darknetresult.md) object.
A [DarknetResult](./bitburner.darknetresult.md) object.
## Remarks

View File

@@ -4,7 +4,7 @@
## NS.getServerUsedRam() method
Get the used RAM on a server.
Get the used RAM on a server. This includes ram used by running scripts as well as blocked ram on darknet servers.
**Signature:**

View File

@@ -1037,7 +1037,7 @@ Get server security level.
</td><td>
Get the used RAM on a server.
Get the used RAM on a server. This includes ram used by running scripts as well as blocked ram on darknet servers.
</td></tr>

View File

@@ -112,5 +112,5 @@ const server = ns.args[0];
const files = ["hack.js", "weaken.js", "grow.js"];
ns.scp(files, server, "home");
```
For password-protected servers (such as darknet servers), a session must be established with the destination server before using this function.
For password-protected servers (such as darknet servers), a session must be established with the destination server before using this function. (The source server does not require a session.)

View File

@@ -181,17 +181,19 @@ export const addRandomConnections = (server: DarknetServer) => {
const x = server.depth;
const y = server.leftOffset;
const horizontalNeighbors = getNeighborsOnRow(x, y);
const lateralConnectionChance = HORIZONTAL_CONNECTION_CHANCE * (1.1 - server.depth * 0.01);
horizontalNeighbors.forEach((neighbor) => {
if (Math.random() < HORIZONTAL_CONNECTION_CHANCE) {
if (Math.random() < lateralConnectionChance) {
connectServers(server, neighbor);
}
});
const serversAbove = getServersOnRowAbove(x);
const serversBelow = getServersOnRowBelow(x);
const verticalConnectionChance = VERTICAL_CONNECTION_CHANCE * (1.1 - server.depth * 0.01);
[...serversAbove, ...serversBelow].forEach((neighbor) => {
const distance = Math.abs(neighbor.depth ?? x - x) + 1;
if (Math.random() < VERTICAL_CONNECTION_CHANCE / distance) {
if (Math.random() < verticalConnectionChance / distance) {
connectServers(server, neighbor);
}
});

View File

@@ -111,7 +111,7 @@ export const mutateDarknet = (): void => {
restartRandomServer();
}
if (Math.random() < 0.5) {
if (Math.random() < 0.3) {
moveRandomDarknetServers(3);
}

View File

@@ -62,22 +62,24 @@ export const handleFailedAuth = (server: DarknetServer, threads: number) => {
* @param person - the player's character
* @param attemptedPassword - the password being attempted
* @param threads - the number of threads used for the password attempt (which speeds up the process)
* @param linear - if true, the time scaling is linear with the number of threads instead of having diminishing returns
*/
export const calculateAuthenticationTime = (
darknetServerData: DarknetServerData,
person: IPerson = Player,
threads = 1,
attemptedPassword = "",
linear = false,
) => {
const chaRequired = darknetServerData.requiredCharismaSkill;
const difficulty = darknetServerData.difficulty;
const baseDiff = (difficulty + 1) * 100;
const diffFactor = 5;
const baseTime = 500;
const baseTime = 850;
const threadsFactor = 1 / (1 + 0.2 * (threads - 1));
const skillFactor = (diffFactor * chaRequired + baseDiff) / (person.skills.charisma + 100);
const threadsFactor = 1 / (linear ? threads : 1 + 0.2 * (threads - 1));
const skillFactor = (diffFactor * chaRequired + baseDiff) / (person.skills.charisma + 150);
const backdoorFactor = getBackdoorAuthTimeDebuff();
const applyUnderleveledFactor = person.skills.charisma <= chaRequired && darknetServerData.depth > 1;
const underleveledFactor = applyUnderleveledFactor ? 1.5 + (chaRequired + 50) / (person.skills.charisma + 50) : 1;
@@ -128,10 +130,9 @@ export const getMultiplierFromCharisma = (scalar = 1) => {
);
};
// TODO: balance xp gain
export const calculatePasswordAttemptChaGain = (server: DarknetServerData, threads: number = 1, success = false) => {
const baseXpGain = 3;
const difficultyBase = 1.12;
const difficultyBase = 0.8;
const xpGain = baseXpGain + difficultyBase ** server.difficulty;
const alreadyHackedMult = server.hasAdminRights ? 0.2 : 1;
const successMult = success && !server.hasAdminRights ? 10 : 1;

View File

@@ -4,7 +4,7 @@ import { generateDarknetServerName, isPasswordResponse, type PasswordResponse }
import { LocationName } from "@enums";
import { getServerState, LogEntry } from "./DarknetState";
import { ModelIds } from "../Enums";
import { getDarknetServer } from "../utils/darknetServerUtils";
import { getDarknetServer, getDarknetServerOrThrow } from "../utils/darknetServerUtils";
import { getAllMovableDarknetServers } from "../utils/darknetNetworkUtils";
import { getExactCorrectChars, getTwoCharsInPassword } from "../utils/darknetAuthUtils";
import type { DarknetServer } from "../../Server/DarknetServer";
@@ -200,8 +200,8 @@ const getLogNoise = (server: DarknetServer, logDate: Date): LogEntry => {
return log(`--${randomServer.password}--`);
}
if (server.modelId === ModelIds.packetSniffer && Math.random() < 0.5 / (server.difficulty + 1)) {
return log("Authentication successful: " + server.password);
if (server.modelId === ModelIds.packetSniffer && Math.random() < 0.7 - server.difficulty * 0.01) {
return addPacketSnifferNoise(server);
}
return log(`${logDate.toLocaleTimeString()}: ${server.hostname} - heartbeat check (alive)`);
@@ -212,6 +212,18 @@ const log = (message: string, pid = -1) => ({
pid,
});
const addPacketSnifferNoise = (server: DarknetServer) => {
const connectedServers = server.serversOnNetwork;
// If the server becomes disconnected while the UI is open and has no neighbors but still
// has logs being populated, fall back to showing the current password
if (Math.random() < 0.3 || connectedServers.length === 0) {
return log(`Logging in with passcode: ${server.password} ...`);
}
const randomServerName = connectedServers[Math.floor(Math.random() * connectedServers.length)];
const randomServer = getDarknetServerOrThrow(randomServerName);
return log(`Connecting to ${randomServer.hostname}:${randomServer.password} ...`);
};
export const getMostRecentAuthLog = (hostname: string) => {
for (const log of getServerState(hostname).serverLogs) {
if (!isPasswordResponse(log.message)) {

View File

@@ -4,11 +4,13 @@ import { AIR_GAP_DEPTH, MS_PER_MUTATION_PER_ROW, NET_WIDTH } from "../Enums";
import { GetAllServers } from "../../Server/AllServers";
import { getNetDepth } from "../effects/labyrinth";
import { CONSTANTS } from "../../Constants";
import { Player } from "@player";
export const getDarknetCyclesPerMutation = () => {
const depth = getNetDepth();
const cycleRate = MS_PER_MUTATION_PER_ROW / CONSTANTS.MilliPerCycle;
return cycleRate / depth;
const rateMultiplier = Player.bitNodeN !== 15 ? 2 : 1;
return (rateMultiplier * cycleRate) / depth;
};
export const getAllOpenPositions = (minDepth: number, maxDepth: number): [number, number][] => {

View File

@@ -425,7 +425,8 @@ export function NetscriptDarknet(): InternalAPI<DarknetAPI> {
}
const server = serverCheck.server;
const networkDelay = calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads) * 4;
const networkDelay =
calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads, "", true) * 4;
const xp = formatNumber(calculatePasswordAttemptChaGain(server, ctx.workerScript.scriptRef.threads), 1);
logger(ctx)(`Captured some outgoing transmissions from ${server.hostname}. (Gained ${xp} cha xp)`);

View File

@@ -4619,7 +4619,7 @@ export interface Darknet {
* @remarks
* RAM cost: 0.1 GB
*
* @returns A promise that resolves to a {@link DarknetResult} object.
* @returns A {@link DarknetResult} object.
*/
unleashStormSeed(): DarknetResult;
@@ -7953,7 +7953,7 @@ export interface NS {
* ns.scp(files, server, "home");
* ```
*
* For password-protected servers (such as darknet servers), a session must be established with the destination server before using this function.
* For password-protected servers (such as darknet servers), a session must be established with the destination server before using this function. (The source server does not require a session.)
*
* @param files - Filename or an array of filenames of script/literature files to copy. Note that if a file is located in a subdirectory, the filename must include the leading `/`.
* @param destination - Hostname/IP of the destination server, which is the server to which the file will be copied.
@@ -8185,7 +8185,7 @@ export interface NS {
*/
getServerMaxRam(host?: string): number;
/**
* Get the used RAM on a server.
* Get the used RAM on a server. This includes ram used by running scripts as well as blocked ram on darknet servers.
* @remarks
* RAM cost: 0.05 GB
*

View File

@@ -17,7 +17,7 @@ import {
} from "../Utilities";
import type { ScriptFilePath } from "../../../src/Paths/ScriptFilePath";
import { DarknetState, getServerState, triggerNextUpdate } from "../../../src/DarkNet/models/DarknetState";
import { getDarknetServerOrThrow } from "../../../src/DarkNet/utils/darknetServerUtils";
import { getDarknetServer, getDarknetServerOrThrow } from "../../../src/DarkNet/utils/darknetServerUtils";
import { ModelIds, ResponseCodeEnum } from "../../../src/DarkNet/Enums";
import { getAllMovableDarknetServers } from "../../../src/DarkNet/utils/darknetNetworkUtils";
import { expectRunningOnDarknetServer } from "../../../src/DarkNet/effects/offlineServerHandling";
@@ -878,9 +878,19 @@ describe("Non-darkweb darknet server", () => {
test("authenticate from darkweb", async () => {
const ns = getNsOnDarkWeb();
const target = getFirstDarknetServerAdjacentToDarkWeb();
const result = await ns.dnet.authenticate(target, getDarknetServerOrThrow(target).password);
expect(result.success).toStrictEqual(true);
const server = getDarknetServerOrThrow(target);
const result = await ns.dnet.authenticate(target, server.password);
// Logging details for debugging flaky test
if (!result.success) {
console.log("Server details grabbed before auth:");
console.log(server);
console.log("result:");
console.log(result);
console.log("currentServerDetails:");
console.log(getDarknetServer(target));
}
expect(result.code).toStrictEqual(ResponseCodeEnum.Success);
expect(result.success).toStrictEqual(true);
});
test("authenticate itself", async () => {
const ns = getNsOnNonDarkwebDarknetServer();