mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-17 14:59:16 +02:00
DNET: Add JS object properties as server names; refactor save/load/server storage to support this (#2482)
This commit is contained in:
committed by
GitHub
parent
b99e522d1c
commit
bab6280735
@@ -135,8 +135,8 @@ export const clearDarknet = (force = false) => {
|
||||
DarknetState.allowMutating = true;
|
||||
DarknetState.openServer = null;
|
||||
DarknetState.stockPromotions = {};
|
||||
DarknetState.migrationInductionServers = {};
|
||||
DarknetState.serverState = {};
|
||||
DarknetState.migrationInductionServers = new Map();
|
||||
DarknetState.serverState = new Map();
|
||||
};
|
||||
|
||||
export const movePlayerIfNeeded = (server?: DarknetServer) => {
|
||||
|
||||
@@ -267,16 +267,17 @@ export const chargeServerMigration = (server: DarknetServer, threads = 1) => {
|
||||
const chargeIncrease = ((Player.skills.charisma + 500) / (server.difficulty * 200 + 1000)) * 0.01 * threads;
|
||||
const xpGained = Player.mults.charisma_exp * 50 * ((200 + Player.skills.charisma) / 200) * threads;
|
||||
Player.gainCharismaExp(xpGained);
|
||||
DarknetState.migrationInductionServers[server.hostname] =
|
||||
(DarknetState.migrationInductionServers[server.hostname] ?? 0) + chargeIncrease;
|
||||
const currentCharge = DarknetState.migrationInductionServers.get(server.hostname) ?? 0;
|
||||
const newCharge = Math.min(currentCharge + chargeIncrease, 1);
|
||||
DarknetState.migrationInductionServers.set(server.hostname, newCharge);
|
||||
const result = {
|
||||
chargeIncrease,
|
||||
newCharge: Math.min(DarknetState.migrationInductionServers[server.hostname], 1),
|
||||
newCharge: newCharge,
|
||||
xpGained: xpGained,
|
||||
};
|
||||
if (DarknetState.migrationInductionServers[server.hostname] >= 1) {
|
||||
if (newCharge >= 1) {
|
||||
moveDarknetServer(server, -2, 4);
|
||||
DarknetState.migrationInductionServers[server.hostname] = 0;
|
||||
DarknetState.migrationInductionServers.set(server.hostname, 0);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -44,12 +44,12 @@ export const DarknetState = {
|
||||
lastStormTime: new Date(),
|
||||
|
||||
stockPromotions: {} as Record<string, number>,
|
||||
migrationInductionServers: {} as Record<string, number>,
|
||||
migrationInductionServers: new Map<string, number>(),
|
||||
|
||||
/**
|
||||
* Do NOT access the server state directly via this property. You must call getServerState.
|
||||
*/
|
||||
serverState: {} as Record<string, ServerState>,
|
||||
serverState: new Map<string, ServerState>(),
|
||||
offlineServers: [] as string[],
|
||||
showFullNetwork: false,
|
||||
zoomIndex: 7,
|
||||
@@ -61,14 +61,17 @@ export const DarknetState = {
|
||||
* Get the server state. It will initialize the state if it does not exist in DarknetState.serverState.
|
||||
*/
|
||||
export const getServerState = (hostname: string): ServerState => {
|
||||
if (!DarknetState.serverState[hostname]) {
|
||||
DarknetState.serverState[hostname] = {
|
||||
serverLogs: [],
|
||||
lastLogTime: undefined,
|
||||
authenticatedPIDs: [],
|
||||
};
|
||||
const currentState = DarknetState.serverState.get(hostname);
|
||||
if (currentState) {
|
||||
return currentState;
|
||||
}
|
||||
return DarknetState.serverState[hostname];
|
||||
const newState = {
|
||||
serverLogs: [],
|
||||
lastLogTime: undefined,
|
||||
authenticatedPIDs: [],
|
||||
};
|
||||
DarknetState.serverState.set(hostname, newState);
|
||||
return newState;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,6 +209,8 @@ export const presetNames = [
|
||||
"Anor_Londo",
|
||||
"The_Painted_World",
|
||||
"The_Depths",
|
||||
"__proto__",
|
||||
"constructor",
|
||||
] as const;
|
||||
|
||||
export const ServerNamePrefixes = [
|
||||
|
||||
@@ -25,7 +25,7 @@ export class RunningScript {
|
||||
|
||||
// Map of [key: hostname] -> Hacking data. Used for offline progress calculations.
|
||||
// Hacking data format: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
dataMap: Record<string, number[]> = {};
|
||||
dataMap: Map<string, number[]> = new Map();
|
||||
|
||||
// Script filename
|
||||
filename = "default.js" as ScriptFilePath;
|
||||
@@ -129,27 +129,23 @@ export class RunningScript {
|
||||
|
||||
// Update the moneyStolen and numTimesHack maps when hacking
|
||||
recordHack(hostname: string, moneyGained: number, n = 1): void {
|
||||
if (this.dataMap[hostname] == null || this.dataMap[hostname].constructor !== Array) {
|
||||
this.dataMap[hostname] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[hostname][0] += moneyGained;
|
||||
this.dataMap[hostname][1] += n;
|
||||
this.initDataMapIfNeeded(hostname);
|
||||
const [hackMoney, hackCount, growCount, weakenCount] = this.dataMap.get(hostname) ?? [];
|
||||
this.dataMap.set(hostname, [hackMoney + moneyGained, hackCount + n, growCount, weakenCount]);
|
||||
}
|
||||
|
||||
// Update the grow map when calling grow()
|
||||
recordGrow(hostname: string, n = 1): void {
|
||||
if (this.dataMap[hostname] == null || this.dataMap[hostname].constructor !== Array) {
|
||||
this.dataMap[hostname] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[hostname][2] += n;
|
||||
this.initDataMapIfNeeded(hostname);
|
||||
const [hackMoney, hackCount, growCount, weakenCount] = this.dataMap.get(hostname) ?? [];
|
||||
this.dataMap.set(hostname, [hackMoney, hackCount, growCount + n, weakenCount]);
|
||||
}
|
||||
|
||||
// Update the weaken map when calling weaken() {
|
||||
recordWeaken(hostname: string, n = 1): void {
|
||||
if (this.dataMap[hostname] == null || this.dataMap[hostname].constructor !== Array) {
|
||||
this.dataMap[hostname] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[hostname][3] += n;
|
||||
this.initDataMapIfNeeded(hostname);
|
||||
const [hackMoney, hackCount, growCount, weakenCount] = this.dataMap.get(hostname) ?? [];
|
||||
this.dataMap.set(hostname, [hackMoney, hackCount, growCount, weakenCount + n]);
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
@@ -157,7 +153,10 @@ export class RunningScript {
|
||||
// Omit the title if it's a ReactNode, it will be filled in with the default on load.
|
||||
return Generic_toJSON(
|
||||
"RunningScript",
|
||||
this,
|
||||
{
|
||||
...this,
|
||||
dataMap: Object.fromEntries(this.dataMap.entries()),
|
||||
},
|
||||
typeof this.title === "string" ? includedProperties : includedPropsNoTitle,
|
||||
);
|
||||
}
|
||||
@@ -165,14 +164,27 @@ export class RunningScript {
|
||||
// Initializes a RunningScript Object from a JSON save state
|
||||
static fromJSON(value: IReviverValue): RunningScript {
|
||||
const runningScript = Generic_fromJSON(RunningScript, value.data, includedProperties);
|
||||
const validEntries = Object.entries(runningScript.dataMap).filter(isValidDataMapEntry);
|
||||
runningScript.dataMap = new Map(validEntries);
|
||||
if (!runningScript.scriptKey) runningScript.scriptKey = scriptKey(runningScript.filename, runningScript.args);
|
||||
if (!runningScript.title) runningScript.title = `${runningScript.filename} ${runningScript.args.join(" ")}`;
|
||||
return runningScript;
|
||||
}
|
||||
|
||||
initDataMapIfNeeded(hostname: string) {
|
||||
if (!this.dataMap.has(hostname)) {
|
||||
this.dataMap.set(hostname, [0, 0, 0, 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const includedProperties = getKeyList(RunningScript, {
|
||||
removedKeys: ["logs", "dependencies", "logUpd", "pid", "parent", "tailProps"],
|
||||
});
|
||||
const includedPropsNoTitle = includedProperties.filter((x) => x !== "title");
|
||||
|
||||
function isValidDataMapEntry(entry: [string, unknown]): entry is [string, number[]] {
|
||||
const [, value] = entry;
|
||||
return Array.isArray(value) && value.length === 4 && value.every((v) => typeof v === "number");
|
||||
}
|
||||
|
||||
constructorsForReviver.RunningScript = RunningScript;
|
||||
|
||||
@@ -32,29 +32,25 @@ export function scriptCalculateOfflineProduction(
|
||||
//Data map: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
|
||||
// Grow
|
||||
for (const hostname of Object.keys(runningScript.dataMap)) {
|
||||
if (Object.hasOwn(runningScript.dataMap, hostname)) {
|
||||
if (runningScript.dataMap[hostname][2] == 0 || runningScript.dataMap[hostname][2] == null) {
|
||||
continue;
|
||||
}
|
||||
const server = GetServer(hostname);
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
const timesGrown = Math.round(
|
||||
((0.5 * runningScript.dataMap[hostname][2]) / runningScript.onlineRunningTime) * timePassed,
|
||||
);
|
||||
runningScript.log(`Called on ${server.hostname} ${timesGrown} times while offline`);
|
||||
const host = GetServer(runningScript.server);
|
||||
if (host === null) {
|
||||
throw new Error("getServer of null key?");
|
||||
}
|
||||
if (!(server instanceof Server)) {
|
||||
throw new Error("trying to grow a non-normal server");
|
||||
}
|
||||
const growth = processSingleServerGrowth(server, timesGrown, host.cpuCores);
|
||||
runningScript.log(`'${server.hostname}' grown by ${formatPercent(growth - 1, 6)} while offline`);
|
||||
for (const [hostname, [, , growCount]] of runningScript.dataMap.entries()) {
|
||||
if (growCount == 0 || growCount == null) {
|
||||
continue;
|
||||
}
|
||||
const server = GetServer(hostname);
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
const timesGrown = Math.round(((0.5 * growCount) / runningScript.onlineRunningTime) * timePassed);
|
||||
runningScript.log(`Called on ${server.hostname} ${timesGrown} times while offline`);
|
||||
const host = GetServer(runningScript.server);
|
||||
if (host === null) {
|
||||
throw new Error("getServer of null key?");
|
||||
}
|
||||
if (!(server instanceof Server)) {
|
||||
throw new Error("trying to grow a non-normal server");
|
||||
}
|
||||
const growth = processSingleServerGrowth(server, timesGrown, host.cpuCores);
|
||||
runningScript.log(`'${server.hostname}' grown by ${formatPercent(growth - 1, 6)} while offline`);
|
||||
}
|
||||
|
||||
// Offline EXP gain
|
||||
@@ -76,26 +72,22 @@ export function scriptCalculateOfflineProduction(
|
||||
runningScript.offlineMoneyMade += moneyGain;
|
||||
|
||||
// Weaken
|
||||
for (const hostname of Object.keys(runningScript.dataMap)) {
|
||||
if (Object.hasOwn(runningScript.dataMap, hostname)) {
|
||||
if (runningScript.dataMap[hostname][3] == 0 || runningScript.dataMap[hostname][3] == null) {
|
||||
continue;
|
||||
}
|
||||
const serv = GetServer(hostname);
|
||||
if (serv == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serv instanceof Server)) throw new Error("trying to weaken a non-normal server");
|
||||
const host = GetServer(runningScript.server);
|
||||
if (host === null) throw new Error("getServer of null key?");
|
||||
const timesWeakened = Math.round(
|
||||
((0.5 * runningScript.dataMap[hostname][3]) / runningScript.onlineRunningTime) * timePassed,
|
||||
);
|
||||
runningScript.log(`Called weaken() on ${serv.hostname} ${timesWeakened} times while offline`);
|
||||
const weakenAmount = getWeakenEffect(runningScript.threads, host.cpuCores);
|
||||
serv.weaken(weakenAmount * timesWeakened);
|
||||
for (const [hostname, [, , , weakenCount]] of runningScript.dataMap.entries()) {
|
||||
if (weakenCount == 0 || weakenCount == null) {
|
||||
continue;
|
||||
}
|
||||
const serv = GetServer(hostname);
|
||||
if (serv == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serv instanceof Server)) throw new Error("trying to weaken a non-normal server");
|
||||
const host = GetServer(runningScript.server);
|
||||
if (host === null) throw new Error("getServer of null key?");
|
||||
const timesWeakened = Math.round(((0.5 * weakenCount) / runningScript.onlineRunningTime) * timePassed);
|
||||
runningScript.log(`Called weaken() on ${serv.hostname} ${timesWeakened} times while offline`);
|
||||
const weakenAmount = getWeakenEffect(runningScript.threads, host.cpuCores);
|
||||
serv.weaken(weakenAmount * timesWeakened);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ import { MAX_NET_DEPTH, NET_WIDTH } from "../DarkNet/Enums";
|
||||
* Key (string) = IP
|
||||
* Value = Server object
|
||||
*/
|
||||
let AllServers: Record<string, Server | HacknetServer | DarknetServer> = {};
|
||||
const AllServers: Map<string, BaseServer> = new Map();
|
||||
|
||||
function GetServerByIP(ip: string): BaseServer | undefined {
|
||||
for (const server of Object.values(AllServers)) {
|
||||
for (const server of AllServers.values()) {
|
||||
if (server.ip !== ip) continue;
|
||||
return server;
|
||||
}
|
||||
@@ -30,17 +30,12 @@ function GetServerByIP(ip: string): BaseServer | undefined {
|
||||
|
||||
//Get server by IP or hostname. Returns null if invalid
|
||||
export function GetServer(s: string): BaseServer | null {
|
||||
if (Object.hasOwn(AllServers, s)) {
|
||||
const server = AllServers[s];
|
||||
if (server) return server;
|
||||
const server = AllServers.get(s);
|
||||
if (server) {
|
||||
return server;
|
||||
}
|
||||
if (!isIPAddress(s)) return null;
|
||||
const ipserver = GetServerByIP(s);
|
||||
if (ipserver !== undefined) {
|
||||
return ipserver;
|
||||
}
|
||||
|
||||
return null;
|
||||
return GetServerByIP(s) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,24 +65,24 @@ export function GetReachableServer(s: string): BaseServer | null {
|
||||
return server;
|
||||
}
|
||||
|
||||
// Get all servers. Only includes darknet servers if showDarkweb is true.
|
||||
export function GetAllServers(showDarkweb = false): BaseServer[] {
|
||||
const servers: BaseServer[] = [];
|
||||
for (const key of Object.keys(AllServers)) {
|
||||
if (!showDarkweb && AllServers[key] instanceof DarknetServer) {
|
||||
for (const server of AllServers.values()) {
|
||||
if (!showDarkweb && server instanceof DarknetServer) {
|
||||
continue;
|
||||
}
|
||||
servers.push(AllServers[key]);
|
||||
servers.push(server);
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
export function DeleteServer(serverkey: string): void {
|
||||
for (const key of Object.keys(AllServers)) {
|
||||
const server = AllServers[key];
|
||||
if (server.ip !== serverkey && server.hostname !== serverkey) continue;
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[key];
|
||||
break;
|
||||
for (const server of AllServers.values()) {
|
||||
if (server.ip === serverkey || server.hostname === serverkey) {
|
||||
AllServers.delete(server.hostname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,8 +101,8 @@ export const disconnectServers = (server1: BaseServer, server2: BaseServer) => {
|
||||
};
|
||||
|
||||
export function ipExists(ip: string): boolean {
|
||||
for (const hostName in AllServers) {
|
||||
if (AllServers[hostName].ip === ip) {
|
||||
for (const server of AllServers.values()) {
|
||||
if (server.ip === ip) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -141,21 +136,20 @@ export function AddToAllServers(server: Server | HacknetServer | DarknetServer):
|
||||
);
|
||||
}
|
||||
|
||||
AllServers[server.hostname] = server;
|
||||
AllServers.set(server.hostname, server);
|
||||
}
|
||||
|
||||
export const renameServer = (hostname: string, newName: string): void => {
|
||||
AllServers[newName] = AllServers[hostname];
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[hostname];
|
||||
const existingServer = AllServers.get(hostname);
|
||||
if (!existingServer) {
|
||||
throw new Error(`Cannot rename server. No server found with hostname ${hostname}`);
|
||||
}
|
||||
AllServers.set(newName, existingServer);
|
||||
AllServers.delete(hostname);
|
||||
};
|
||||
|
||||
export function prestigeAllServers(): void {
|
||||
for (const member of Object.keys(AllServers)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete AllServers[member];
|
||||
}
|
||||
AllServers = {};
|
||||
AllServers.clear();
|
||||
// WIP: Check other properties in DarknetState as well, then improve validateDarknetNetwork.
|
||||
DarknetState.Network = new Array(MAX_NET_DEPTH)
|
||||
.fill(null)
|
||||
@@ -168,18 +162,19 @@ export function loadAllServers(saveString: string): void {
|
||||
if (Object.keys(allServersData).length === 0) {
|
||||
throw new Error("Server list is empty.");
|
||||
}
|
||||
AllServers.clear();
|
||||
for (const [serverName, server] of Object.entries(allServersData)) {
|
||||
if (!(server instanceof Server) && !(server instanceof HacknetServer) && !(server instanceof DarknetServer)) {
|
||||
throw new Error(`Server ${serverName} is not an instance of Server or HacknetServer.`);
|
||||
throw new Error(`Server ${serverName} is not an instance of Server or HacknetServer or DarknetServer.`);
|
||||
} else {
|
||||
AllServers.set(serverName, server);
|
||||
}
|
||||
}
|
||||
// We validated the data above, so it's safe to typecast here.
|
||||
AllServers = allServersData as typeof AllServers;
|
||||
|
||||
// Apply blocked ram for darknet servers
|
||||
applyRamBlocks();
|
||||
}
|
||||
|
||||
export function saveAllServers(): string {
|
||||
return JSON.stringify(AllServers);
|
||||
return JSON.stringify(Object.fromEntries(AllServers.entries()));
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ function LogWindow({ hidden, script, onClose }: LogWindowProps): React.ReactElem
|
||||
}
|
||||
// Reset some things, because we're reusing the RunningScript instance
|
||||
script.ramUsage = ramUsage;
|
||||
script.dataMap = {};
|
||||
script.dataMap = new Map();
|
||||
script.onlineExpGained = 0;
|
||||
script.onlineMoneyMade = 0;
|
||||
script.onlineRunningTime = 0.01;
|
||||
|
||||
Reference in New Issue
Block a user