mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-21 00:32:51 +02:00
NETSCRIPT: Greatly speed up script launching, and remove the limitation unique args per script (#440)
* Remove the limitation unique args per script * Internal changes to how runningScripts are stored on the server, to make common usage faster.
This commit is contained in:
@@ -10,10 +10,9 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
import { SpecialServers } from "./data/SpecialServers";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import "../Script/RunningScript"; // For reviver side-effect
|
||||
|
||||
import { IPAddress, isIPAddress } from "../Types/strings";
|
||||
import type { RunningScript } from "../Script/RunningScript";
|
||||
|
||||
import "../Script/RunningScript"; // For reviver side-effect
|
||||
|
||||
/**
|
||||
* Map of all Servers that exist in the game
|
||||
@@ -207,17 +206,6 @@ function excludeReplacer(key: string, value: any): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
function scriptFilter(script: RunningScript): boolean {
|
||||
return !script.temporary;
|
||||
}
|
||||
|
||||
function includeReplacer(key: string, value: any): any {
|
||||
if (key === "runningScripts") {
|
||||
return value.filter(scriptFilter);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function saveAllServers(excludeRunningScripts = false): string {
|
||||
return JSON.stringify(AllServers, excludeRunningScripts ? excludeReplacer : includeReplacer);
|
||||
return JSON.stringify(AllServers, excludeRunningScripts ? excludeReplacer : undefined);
|
||||
}
|
||||
|
||||
+55
-17
@@ -7,10 +7,10 @@ import { IReturnStatus } from "../types";
|
||||
|
||||
import { ScriptFilePath, hasScriptExtension } from "../Paths/ScriptFilePath";
|
||||
import { TextFilePath, hasTextExtension } from "../Paths/TextFilePath";
|
||||
import { Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
|
||||
import { matchScriptPathExact } from "../utils/helpers/scriptKey";
|
||||
|
||||
import { createRandomIp } from "../utils/IPAddress";
|
||||
import { compareArrays } from "../utils/helpers/compareArrays";
|
||||
import { ScriptArg } from "../Netscript/ScriptArg";
|
||||
import { JSONMap } from "../Types/Jsonable";
|
||||
import { IPAddress, ServerName } from "../Types/strings";
|
||||
import { FilePath } from "../Paths/FilePath";
|
||||
@@ -19,6 +19,10 @@ import { ProgramFilePath, hasProgramExtension } from "../Paths/ProgramFilePath";
|
||||
import { MessageFilename } from "src/Message/MessageHelpers";
|
||||
import { LiteratureName } from "src/Literature/data/LiteratureNames";
|
||||
import { CompletedProgramName } from "src/Programs/Programs";
|
||||
import { getKeyList } from "../utils/helpers/getKeyList";
|
||||
import lodash from "lodash";
|
||||
|
||||
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
@@ -64,7 +68,7 @@ export abstract class BaseServer implements IServer {
|
||||
maxRam = 0;
|
||||
|
||||
// Message files AND Literature files on this Server
|
||||
messages: (MessageFilename | LiteratureName | FilePath)[] = [];
|
||||
messages: (MessageFilename | LiteratureName)[] = [];
|
||||
|
||||
// Name of company/faction/etc. that this server belongs to.
|
||||
// Optional, not applicable to all Servers
|
||||
@@ -77,8 +81,12 @@ export abstract class BaseServer implements IServer {
|
||||
// RAM (GB) used. i.e. unavailable RAM
|
||||
ramUsed = 0;
|
||||
|
||||
// RunningScript files on this server
|
||||
runningScripts: RunningScript[] = [];
|
||||
// RunningScript files on this server. Keyed first by name/args, then by PID.
|
||||
runningScriptMap: Map<ScriptKey, Map<number, RunningScript>> = new Map();
|
||||
|
||||
// RunningScript files loaded from the savegame. Only stored here temporarily,
|
||||
// this field is undef while the game is running.
|
||||
savedScripts: RunningScript[] | undefined = undefined;
|
||||
|
||||
// Script files on this Server
|
||||
scripts: JSONMap<ScriptFilePath, Script> = new JSONMap();
|
||||
@@ -138,23 +146,16 @@ export abstract class BaseServer implements IServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Find an actively running script on this server by filepath and args. */
|
||||
getRunningScript(path: ScriptFilePath, scriptArgs: ScriptArg[]): RunningScript | null {
|
||||
for (const rs of this.runningScripts) {
|
||||
if (rs.filename === path && compareArrays(rs.args, scriptArgs)) return rs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Get a TextFile or Script depending on the input path type. */
|
||||
getContentFile(path: ContentFilePath): ContentFile | null {
|
||||
return (hasTextExtension(path) ? this.textFiles.get(path) : this.scripts.get(path)) ?? null;
|
||||
}
|
||||
|
||||
/** Returns boolean indicating whether the given script is running on this server */
|
||||
isRunning(fn: string): boolean {
|
||||
for (const runningScriptObj of this.runningScripts) {
|
||||
if (runningScriptObj.filename === fn) {
|
||||
isRunning(path: ScriptFilePath): boolean {
|
||||
const pattern = matchScriptPathExact(lodash.escapeRegExp(path));
|
||||
for (const k of this.runningScriptMap.keys()) {
|
||||
if (pattern.test(k)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -216,7 +217,12 @@ export abstract class BaseServer implements IServer {
|
||||
* be run.
|
||||
*/
|
||||
runScript(script: RunningScript): void {
|
||||
this.runningScripts.push(script);
|
||||
let byPid = this.runningScriptMap.get(script.scriptKey);
|
||||
if (!byPid) {
|
||||
byPid = new Map();
|
||||
this.runningScriptMap.set(script.scriptKey, byPid);
|
||||
}
|
||||
byPid.set(script.pid, script);
|
||||
}
|
||||
|
||||
setMaxRam(ram: number): void {
|
||||
@@ -279,4 +285,36 @@ export abstract class BaseServer implements IServer {
|
||||
if (hasTextExtension(path)) return this.writeToTextFile(path, content);
|
||||
return this.writeToScriptFile(path, content);
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
// Called by subclasses, not stringify.
|
||||
toJSONBase(ctorName: string, keys: readonly (keyof this)[]): IReviverValue {
|
||||
// RunningScripts are stored as a simple array, both for backward compatibility,
|
||||
// compactness, and ease of filtering them here.
|
||||
const result = Generic_toJSON(ctorName, this, keys);
|
||||
|
||||
const rsArray: RunningScript[] = [];
|
||||
for (const byPid of this.runningScriptMap.values()) {
|
||||
for (const rs of byPid.values()) {
|
||||
if (!rs.temporary) {
|
||||
rsArray.push(rs);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.data.runningScripts = rsArray;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initializes a Server Object from a JSON save state
|
||||
// Called by subclasses, not Reviver.
|
||||
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
|
||||
const result = Generic_fromJSON(ctor, value.data, keys);
|
||||
result.savedScripts = value.data.runningScripts;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Customize a prune list for a subclass.
|
||||
static getIncludedKeys<T extends BaseServer>(ctor: new () => T): readonly (keyof T)[] {
|
||||
return getKeyList(ctor, { removedKeys: ["runningScriptMap", "savedScripts", "ramUsed"] });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
import { createRandomString } from "../utils/helpers/createRandomString";
|
||||
import { createRandomIp } from "../utils/IPAddress";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { IPAddress, ServerName } from "../Types/strings";
|
||||
|
||||
export interface IConstructorParams {
|
||||
@@ -147,13 +147,14 @@ export class Server extends BaseServer {
|
||||
|
||||
/** Serialize the current object to a JSON save state */
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("Server", this);
|
||||
return this.toJSONBase("Server", includedKeys);
|
||||
}
|
||||
|
||||
// Initializes a Server Object from a JSON save state
|
||||
static fromJSON(value: IReviverValue): Server {
|
||||
return Generic_fromJSON(Server, value.data);
|
||||
return BaseServer.fromJSONBase(value, Server, includedKeys);
|
||||
}
|
||||
}
|
||||
const includedKeys = BaseServer.getIncludedKeys(Server);
|
||||
|
||||
constructorsForReviver.Server = Server;
|
||||
|
||||
@@ -252,7 +252,6 @@ export function prestigeHomeComputer(homeComp: Server): void {
|
||||
const hasBitflume = homeComp.programs.includes(CompletedProgramName.bitFlume);
|
||||
|
||||
homeComp.programs.length = 0; //Remove programs
|
||||
homeComp.runningScripts = [];
|
||||
homeComp.serversOnNetwork = [];
|
||||
homeComp.isConnectedTo = true;
|
||||
homeComp.ramUsed = 0;
|
||||
@@ -263,6 +262,9 @@ export function prestigeHomeComputer(homeComp: Server): void {
|
||||
|
||||
homeComp.messages.length = 0; //Remove .lit and .msg files
|
||||
homeComp.messages.push(LiteratureName.HackersStartingHandbook);
|
||||
if (homeComp.runningScriptMap.size !== 0) {
|
||||
throw new Error("All programs weren't already killed!");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the i-th server on the specified server's network
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Player } from "@player";
|
||||
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
|
||||
// Returns the cost of purchasing a server with the given RAM
|
||||
@@ -72,12 +73,16 @@ export const renamePurchasedServer = (hostname: string, newName: string): void =
|
||||
const home = Player.getHomeComputer();
|
||||
home.serversOnNetwork = replace(home.serversOnNetwork, hostname, newName);
|
||||
server.serversOnNetwork = replace(server.serversOnNetwork, hostname, newName);
|
||||
server.runningScripts.forEach((r) => (r.server = newName));
|
||||
for (const byPid of server.runningScriptMap.values()) {
|
||||
for (const r of byPid.values()) {
|
||||
r.server = newName;
|
||||
// Lookup can't fail.
|
||||
const ws = workerScripts.get(r.pid) as WorkerScript;
|
||||
ws.hostname = newName;
|
||||
}
|
||||
}
|
||||
server.scripts.forEach((r) => (r.server = newName));
|
||||
server.hostname = newName;
|
||||
workerScripts.forEach((w) => {
|
||||
if (w.hostname === hostname) w.hostname = newName;
|
||||
});
|
||||
renameServer(hostname, newName);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user