mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-17 21:23:54 +01:00
CODEBASE: Add Jsonable Map and Set types, move player.sourceFiles to a map (#473)
This commit is contained in:
parent
c44bdc1018
commit
0df984eea0
@ -66,11 +66,11 @@ function bitNodeFinishedState(): boolean {
|
||||
}
|
||||
|
||||
function hasAccessToSF(player: PlayerObject, bn: number): boolean {
|
||||
return player.bitNodeN === bn || player.sourceFiles.some((a) => a.n === bn);
|
||||
return player.bitNodeN === bn || player.sourceFileLvl(bn) > 0;
|
||||
}
|
||||
|
||||
function knowsAboutBitverse(player: PlayerObject): boolean {
|
||||
return player.sourceFiles.some((a) => a.n === 1);
|
||||
return player.sourceFiles.size > 0;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@ -274,7 +274,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
NS2: {
|
||||
...achievementData["NS2"],
|
||||
Icon: "ns2",
|
||||
Condition: () => Player.getHomeComputer().scripts.some((s) => s.filename.endsWith(".js")),
|
||||
Condition: () => [...Player.getHomeComputer().scripts.values()].some((s) => s.filename.endsWith(".js")),
|
||||
},
|
||||
FROZE: {
|
||||
...achievementData["FROZE"],
|
||||
@ -317,7 +317,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
SCRIPTS_30: {
|
||||
...achievementData["SCRIPTS_30"],
|
||||
Icon: "folders",
|
||||
Condition: () => Player.getHomeComputer().scripts.length >= 30,
|
||||
Condition: () => Player.getHomeComputer().scripts.size >= 30,
|
||||
},
|
||||
KARMA_1000000: {
|
||||
...achievementData["KARMA_1000000"],
|
||||
@ -342,7 +342,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
SCRIPT_32GB: {
|
||||
...achievementData["SCRIPT_32GB"],
|
||||
Icon: "bigcost",
|
||||
Condition: () => Player.getHomeComputer().scripts.some((s) => (s.ramUsage ?? 0) >= 32),
|
||||
Condition: () => [...Player.getHomeComputer().scripts.values()].some((s) => (s.ramUsage ?? 0) >= 32),
|
||||
},
|
||||
FIRST_HACKNET_NODE: {
|
||||
...achievementData["FIRST_HACKNET_NODE"],
|
||||
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* React Component for displaying a list of the player's Source-Files
|
||||
* on the Augmentations UI
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Player } from "@player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||
|
||||
import { SourceFileAccordion } from "../../ui/React/SourceFileAccordion";
|
||||
|
||||
export function OwnedSourceFiles(): React.ReactElement {
|
||||
const sourceSfs = Player.sourceFiles.slice();
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceSfs.sort((sf1, sf2) => {
|
||||
return sf1.n - sf2.n;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{sourceSfs.map((e) => {
|
||||
const srcFileKey = "SourceFile" + e.n;
|
||||
const sfObj = SourceFiles[srcFileKey];
|
||||
if (sfObj == null) {
|
||||
console.error(`Invalid source file number: ${e.n}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SourceFileAccordion key={e.n} level={e.lvl} sf={sfObj} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
@ -71,27 +71,24 @@ const getMaxLevel = (sfObj: SourceFile | SfMinus1): string | number => {
|
||||
};
|
||||
|
||||
export function SourceFilesElement(): React.ReactElement {
|
||||
const sourceSfs = Player.sourceFiles.slice();
|
||||
const sourceFilesCopy = new Map(Player.sourceFiles);
|
||||
const exploits = Player.exploits;
|
||||
// Create a fake SF for -1, if "owned"
|
||||
if (exploits.length > 0) {
|
||||
sourceSfs.unshift({
|
||||
n: -1,
|
||||
lvl: exploits.length,
|
||||
});
|
||||
sourceFilesCopy.set(-1, exploits.length);
|
||||
}
|
||||
|
||||
const sfList = [...sourceFilesCopy];
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceSfs.sort((sf1, sf2) => {
|
||||
return sf1.n - sf2.n;
|
||||
});
|
||||
sfList.sort(([n1, __lvl1], [n2, __lvl2]) => n1 - n2);
|
||||
}
|
||||
|
||||
if (sourceSfs.length === 0) {
|
||||
if (sfList.length === 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const [selectedSf, setSelectedSf] = useState(sourceSfs[0]);
|
||||
const firstEle = sfList[0];
|
||||
const [selectedSf, setSelectedSf] = useState({ n: firstEle[0], lvl: firstEle[1] });
|
||||
|
||||
return (
|
||||
<Box sx={{ width: "100%", mt: 1 }}>
|
||||
@ -104,8 +101,8 @@ export function SourceFilesElement(): React.ReactElement {
|
||||
sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}
|
||||
disablePadding
|
||||
>
|
||||
{sourceSfs.map((e, i) => {
|
||||
const sfObj = safeGetSf(e.n);
|
||||
{sfList.map(([n, lvl], i) => {
|
||||
const sfObj = safeGetSf(n);
|
||||
if (!sfObj) return;
|
||||
|
||||
const maxLevel = getMaxLevel(sfObj);
|
||||
@ -113,8 +110,8 @@ export function SourceFilesElement(): React.ReactElement {
|
||||
return (
|
||||
<ListItemButton
|
||||
key={i + 1}
|
||||
onClick={() => setSelectedSf(e)}
|
||||
selected={selectedSf.n === e.n}
|
||||
onClick={() => setSelectedSf({ n, lvl })}
|
||||
selected={selectedSf.n === n}
|
||||
sx={{ py: 0 }}
|
||||
>
|
||||
<ListItemText
|
||||
@ -122,7 +119,7 @@ export function SourceFilesElement(): React.ReactElement {
|
||||
primary={<Typography>{sfObj.name}</Typography>}
|
||||
secondary={
|
||||
<Typography>
|
||||
Level {e.lvl} / {maxLevel}
|
||||
Level {lvl} / {maxLevel}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
|
@ -7,7 +7,6 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { PlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
|
||||
import { Player } from "@player";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
|
||||
@ -21,20 +20,10 @@ export function SourceFiles(): React.ReactElement {
|
||||
Player.hacknetNodes = [];
|
||||
}
|
||||
if (sfLvl === 0) {
|
||||
Player.sourceFiles = Player.sourceFiles.filter((sf) => sf.n !== sfN);
|
||||
Player.sourceFiles.delete(sfN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Player.sourceFiles.some((sf) => sf.n === sfN)) {
|
||||
Player.sourceFiles.push(new PlayerOwnedSourceFile(sfN, sfLvl));
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < Player.sourceFiles.length; i++) {
|
||||
if (Player.sourceFiles[i].n === sfN) {
|
||||
Player.sourceFiles[i].lvl = sfLvl;
|
||||
}
|
||||
}
|
||||
Player.sourceFiles.set(sfN, sfLvl);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,16 +15,17 @@ import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import { ServerName } from "../Types/strings";
|
||||
|
||||
interface IServerProps {
|
||||
hostname: string;
|
||||
hostname: ServerName;
|
||||
}
|
||||
|
||||
function ServerAccordion(props: IServerProps): React.ReactElement {
|
||||
const server = GetServer(props.hostname);
|
||||
if (server === null) throw new Error(`server '${props.hostname}' should not be null`);
|
||||
let totalSize = 0;
|
||||
for (const f of server.scripts) {
|
||||
for (const f of server.scripts.values()) {
|
||||
totalSize += f.code.length;
|
||||
}
|
||||
|
||||
@ -43,7 +44,7 @@ function ServerAccordion(props: IServerProps): React.ReactElement {
|
||||
|
||||
const files: File[] = [];
|
||||
|
||||
for (const f of server.scripts) {
|
||||
for (const f of server.scripts.values()) {
|
||||
files.push({ name: f.filename, size: f.code.length });
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ function initWebserver(): void {
|
||||
return {
|
||||
res: true,
|
||||
data: {
|
||||
files: home.scripts.map((script) => ({
|
||||
files: [...home.scripts.values()].map((script) => ({
|
||||
filename: script.filename,
|
||||
code: script.code,
|
||||
ramUsage: script.ramUsage,
|
||||
|
@ -93,7 +93,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
function EatNoodles(): void {
|
||||
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", ToastVariant.SUCCESS, 2000);
|
||||
N00dles(); // This is the true power of the noodles.
|
||||
if (Player.sourceFiles.length > 0) Player.giveExploit(Exploit.N00dles);
|
||||
if (Player.sourceFiles.size > 0) Player.giveExploit(Exploit.N00dles);
|
||||
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
||||
Player.exp.intelligence *= 1.0000000000000002;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ function checkForMessagesToSend(): void {
|
||||
}
|
||||
//If the daemon can be hacked, send the player icarus.msg
|
||||
if (Player.skills.hacking >= worldDaemon.requiredHackingSkill) {
|
||||
sendMessage(redpill, Player.sourceFiles.length === 0);
|
||||
sendMessage(redpill, Player.sourceFiles.size === 0);
|
||||
}
|
||||
//If the daemon cannot be hacked, send the player truthgazer.msg a single time.
|
||||
else if (!recvd(truthGazer)) {
|
||||
|
@ -96,16 +96,11 @@ export class WorkerScript {
|
||||
if (server == null) {
|
||||
throw new Error(`WorkerScript constructed with invalid server ip: ${this.hostname}`);
|
||||
}
|
||||
let found = false;
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === this.name) {
|
||||
found = true;
|
||||
this.code = server.scripts[i].code;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
const script = server.scripts.get(this.name);
|
||||
if (!script) {
|
||||
throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`);
|
||||
}
|
||||
this.code = script.code;
|
||||
this.scriptRef = runningScriptObj;
|
||||
this.args = runningScriptObj.args.slice();
|
||||
this.env = new Environment();
|
||||
@ -127,16 +122,14 @@ export class WorkerScript {
|
||||
*/
|
||||
getScript(): Script | null {
|
||||
const server = this.getServer();
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === this.name) {
|
||||
return server.scripts[i];
|
||||
}
|
||||
const script = server.scripts.get(this.name);
|
||||
if (!script) {
|
||||
console.error(
|
||||
"Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.error(
|
||||
"Failed to find underlying Script object in WorkerScript.getScript(). This probably means somethings wrong",
|
||||
);
|
||||
return null;
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,17 +137,7 @@ export class WorkerScript {
|
||||
* or null if it cannot be found
|
||||
*/
|
||||
getScriptOnServer(fn: string, server: BaseServer): Script | null {
|
||||
if (server == null) {
|
||||
server = this.getServer();
|
||||
}
|
||||
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === fn) {
|
||||
return server.scripts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return server.scripts.get(fn) ?? null;
|
||||
}
|
||||
|
||||
shouldLog(fn: string): boolean {
|
||||
|
@ -36,7 +36,7 @@ import {
|
||||
} from "./Server/ServerPurchases";
|
||||
import { Server } from "./Server/Server";
|
||||
import { influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing";
|
||||
import { areFilesEqual, isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { TextFile, getTextFile, createTextFile } from "./TextFile";
|
||||
import { runScriptFromScript } from "./NetscriptWorker";
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
@ -877,7 +877,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
}
|
||||
|
||||
// Scp for script files
|
||||
const sourceScript = sourceServ.scripts.find((script) => script.filename === file);
|
||||
const sourceScript = sourceServ.scripts.get(file);
|
||||
if (!sourceScript) {
|
||||
helpers.log(ctx, () => `File '${file}' does not exist.`);
|
||||
noFailures = false;
|
||||
@ -885,7 +885,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
}
|
||||
|
||||
// Overwrite script if it already exists
|
||||
const destScript = destServer.scripts.find((script) => script.filename === file);
|
||||
const destScript = destServer.scripts.get(file);
|
||||
if (destScript) {
|
||||
if (destScript.code === sourceScript.code) {
|
||||
helpers.log(ctx, () => `Identical file '${file}' was already on '${destServer?.hostname}'`);
|
||||
@ -900,7 +900,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
|
||||
// Create new script if it does not already exist
|
||||
const newScript = new Script(file, sourceScript.code, destServer.hostname);
|
||||
destServer.scripts.push(newScript);
|
||||
destServer.scripts.set(file, newScript);
|
||||
helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`);
|
||||
}
|
||||
|
||||
@ -915,7 +915,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
...server.contracts.map((contract) => contract.fn),
|
||||
...server.messages,
|
||||
...server.programs,
|
||||
...server.scripts.map((script) => script.filename),
|
||||
...server.scripts.keys(),
|
||||
...server.textFiles.map((textFile) => textFile.filename),
|
||||
];
|
||||
|
||||
@ -1147,11 +1147,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
const filename = helpers.string(ctx, "filename", _filename);
|
||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||
const server = helpers.getServer(ctx, hostname);
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (filename == server.scripts[i].filename) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (server.scripts.has(filename)) return true;
|
||||
for (let i = 0; i < server.programs.length; ++i) {
|
||||
if (filename.toLowerCase() == server.programs[i].toLowerCase()) {
|
||||
return true;
|
||||
@ -1366,47 +1362,45 @@ export const ns: InternalAPI<NSFull> = {
|
||||
}
|
||||
return writePort(portNumber, data);
|
||||
},
|
||||
write:
|
||||
(ctx) =>
|
||||
(_filename, _data = "", _mode = "a") => {
|
||||
let fn = helpers.string(ctx, "handle", _filename);
|
||||
const data = helpers.string(ctx, "data", _data);
|
||||
const mode = helpers.string(ctx, "mode", _mode);
|
||||
if (!isValidFilePath(fn)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`);
|
||||
write: (ctx) => (_filename, _data, _mode) => {
|
||||
let filename = helpers.string(ctx, "handle", _filename);
|
||||
const data = helpers.string(ctx, "data", _data ?? "");
|
||||
const mode = helpers.string(ctx, "mode", _mode ?? "a");
|
||||
if (!isValidFilePath(filename)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${filename}`);
|
||||
|
||||
if (fn.lastIndexOf("/") === 0) fn = removeLeadingSlash(fn);
|
||||
if (filename.lastIndexOf("/") === 0) filename = removeLeadingSlash(filename);
|
||||
|
||||
const server = helpers.getServer(ctx, ctx.workerScript.hostname);
|
||||
const server = helpers.getServer(ctx, ctx.workerScript.hostname);
|
||||
|
||||
if (isScriptFilename(fn)) {
|
||||
// Write to script
|
||||
let script = ctx.workerScript.getScriptOnServer(fn, server);
|
||||
if (script == null) {
|
||||
// Create a new script
|
||||
script = new Script(fn, String(data), server.hostname);
|
||||
server.scripts.push(script);
|
||||
return;
|
||||
}
|
||||
mode === "w" ? (script.code = String(data)) : (script.code += data);
|
||||
// Set ram to null so a recalc is performed the next time ram usage is needed
|
||||
script.invalidateModule();
|
||||
if (isScriptFilename(filename)) {
|
||||
// Write to script
|
||||
let script = ctx.workerScript.getScriptOnServer(filename, server);
|
||||
if (!script) {
|
||||
// Create a new script
|
||||
script = new Script(filename, String(data), server.hostname);
|
||||
server.scripts.set(filename, script);
|
||||
return;
|
||||
} else {
|
||||
// Write to text file
|
||||
if (!fn.endsWith(".txt")) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: ${fn}`);
|
||||
const txtFile = getTextFile(fn, server);
|
||||
if (txtFile == null) {
|
||||
createTextFile(fn, String(data), server);
|
||||
return;
|
||||
}
|
||||
if (mode === "w") {
|
||||
txtFile.write(String(data));
|
||||
} else {
|
||||
txtFile.append(String(data));
|
||||
}
|
||||
}
|
||||
mode === "w" ? (script.code = data) : (script.code += data);
|
||||
// Set ram to null so a recalc is performed the next time ram usage is needed
|
||||
script.invalidateModule();
|
||||
return;
|
||||
},
|
||||
} else {
|
||||
// Write to text file
|
||||
if (!filename.endsWith(".txt")) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: ${filename}`);
|
||||
const txtFile = getTextFile(filename, server);
|
||||
if (txtFile == null) {
|
||||
createTextFile(filename, String(data), server);
|
||||
return;
|
||||
}
|
||||
if (mode === "w") {
|
||||
txtFile.write(String(data));
|
||||
} else {
|
||||
txtFile.append(String(data));
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
tryWritePort: (ctx) => (_portNumber, data) => {
|
||||
const portNumber = helpers.portNumber(ctx, _portNumber);
|
||||
if (typeof data !== "string" && typeof data !== "number") {
|
||||
@ -1515,21 +1509,19 @@ export const ns: InternalAPI<NSFull> = {
|
||||
getScriptName: (ctx) => () => {
|
||||
return ctx.workerScript.name;
|
||||
},
|
||||
getScriptRam:
|
||||
(ctx) =>
|
||||
(_scriptname, _hostname = ctx.workerScript.hostname) => {
|
||||
const scriptname = helpers.string(ctx, "scriptname", _scriptname);
|
||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||
const server = helpers.getServer(ctx, hostname);
|
||||
const script = server.scripts.find((serverScript) => areFilesEqual(serverScript.filename, scriptname));
|
||||
if (!script) return 0;
|
||||
const ramUsage = script.getRamUsage(server.scripts);
|
||||
if (!ramUsage) {
|
||||
helpers.log(ctx, () => `Could not calculate ram usage for ${scriptname} on ${hostname}.`);
|
||||
return 0;
|
||||
}
|
||||
return ramUsage;
|
||||
},
|
||||
getScriptRam: (ctx) => (_scriptname, _hostname) => {
|
||||
const scriptname = helpers.string(ctx, "scriptname", _scriptname);
|
||||
const hostname = helpers.string(ctx, "hostname", _hostname ?? ctx.workerScript.hostname);
|
||||
const server = helpers.getServer(ctx, hostname);
|
||||
const script = server.scripts.get(scriptname);
|
||||
if (!script) return 0;
|
||||
const ramUsage = script.getRamUsage(server.scripts);
|
||||
if (!ramUsage) {
|
||||
helpers.log(ctx, () => `Could not calculate ram usage for ${scriptname} on ${hostname}.`);
|
||||
return 0;
|
||||
}
|
||||
return ramUsage;
|
||||
},
|
||||
getRunningScript:
|
||||
(ctx) =>
|
||||
(fn, hostname, ...args) => {
|
||||
@ -1781,7 +1773,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
}; // Wrap the user function to prevent WorkerScript leaking as 'this'
|
||||
},
|
||||
mv: (ctx) => (_host, _source, _destination) => {
|
||||
const host = helpers.string(ctx, "host", _host);
|
||||
const hostname = helpers.string(ctx, "host", _host);
|
||||
const source = helpers.string(ctx, "source", _source);
|
||||
const destination = helpers.string(ctx, "destination", _destination);
|
||||
|
||||
@ -1800,44 +1792,40 @@ export const ns: InternalAPI<NSFull> = {
|
||||
return;
|
||||
}
|
||||
|
||||
const destServer = helpers.getServer(ctx, host);
|
||||
const server = helpers.getServer(ctx, hostname);
|
||||
|
||||
if (!source_is_txt && destServer.isRunning(source))
|
||||
if (!source_is_txt && server.isRunning(source))
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, `Cannot use 'mv' on a script that is running`);
|
||||
|
||||
interface File {
|
||||
filename: string;
|
||||
}
|
||||
let source_file: File | undefined;
|
||||
let dest_file: File | undefined;
|
||||
|
||||
const files = source_is_txt ? destServer.textFiles : destServer.scripts;
|
||||
let source_file: File | null = null;
|
||||
let dest_file: File | null = null;
|
||||
|
||||
for (let i = 0; i < files.length; ++i) {
|
||||
const file = files[i];
|
||||
if (file.filename === source) {
|
||||
source_file = file;
|
||||
} else if (file.filename === destination) {
|
||||
dest_file = file;
|
||||
}
|
||||
if (source_is_txt) {
|
||||
// Traverses twice potentially. Inefficient but will soon be replaced with a map.
|
||||
source_file = server.textFiles.find((textFile) => textFile.filename === source);
|
||||
dest_file = server.textFiles.find((textFile) => textFile.filename === destination);
|
||||
} else {
|
||||
source_file = server.scripts.get(source);
|
||||
dest_file = server.scripts.get(destination);
|
||||
}
|
||||
if (!source_file) throw helpers.makeRuntimeErrorMsg(ctx, `Source file ${source} does not exist`);
|
||||
|
||||
if (source_file == null) throw helpers.makeRuntimeErrorMsg(ctx, `Source file ${source} does not exist`);
|
||||
|
||||
if (dest_file != null) {
|
||||
if (dest_file) {
|
||||
if (dest_file instanceof TextFile && source_file instanceof TextFile) {
|
||||
dest_file.text = source_file.text;
|
||||
} else if (dest_file instanceof Script && source_file instanceof Script) {
|
||||
dest_file.code = source_file.code;
|
||||
// Source needs to be invalidated as well, to invalidate its dependents
|
||||
source_file.invalidateModule();
|
||||
dest_file.invalidateModule();
|
||||
}
|
||||
|
||||
destServer.removeFile(source);
|
||||
server.removeFile(source);
|
||||
} else {
|
||||
source_file.filename = destination;
|
||||
if (source_file instanceof Script) {
|
||||
source_file.invalidateModule();
|
||||
}
|
||||
if (source_file instanceof Script) source_file.invalidateModule();
|
||||
}
|
||||
},
|
||||
flags: Flags,
|
||||
|
@ -15,7 +15,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
||||
return;
|
||||
};
|
||||
const getBladeburner = function (ctx: NetscriptContext): Bladeburner {
|
||||
const apiAccess = Player.bitNodeN === 7 || Player.sourceFiles.some((a) => a.n === 7);
|
||||
const apiAccess = Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0;
|
||||
if (!apiAccess) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "You have not unlocked the bladeburner API.", "API ACCESS");
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
|
||||
numPeopleKilled: 0,
|
||||
money: 0,
|
||||
city: CityName.Sector12,
|
||||
location: "",
|
||||
location: LocationName.TravelAgency,
|
||||
bitNodeN: 0,
|
||||
totalPlaytime: 0,
|
||||
jobs: {},
|
||||
|
@ -50,7 +50,6 @@ import { canGetBonus, onExport } from "../ExportBonus";
|
||||
import { saveObject } from "../SaveObject";
|
||||
import { calculateCrimeWorkStats } from "../Work/Formulas";
|
||||
import { findEnumMember } from "../utils/helpers/enum";
|
||||
import { areFilesEqual } from "../Terminal/DirectoryHelpers";
|
||||
import { Engine } from "../engine";
|
||||
import { checkEnum } from "../utils/helpers/enum";
|
||||
|
||||
@ -81,7 +80,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
//Run a script after reset
|
||||
if (!cbScript) return;
|
||||
const home = Player.getHomeComputer();
|
||||
const script = home.scripts.find((serverScript) => areFilesEqual(serverScript.filename, cbScript));
|
||||
const script = home.scripts.get(cbScript);
|
||||
if (!script) return;
|
||||
const ramUsage = script.getRamUsage(home.scripts);
|
||||
if (!ramUsage) {
|
||||
@ -110,9 +109,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
return res;
|
||||
},
|
||||
getOwnedSourceFiles: () => () => {
|
||||
return Player.sourceFiles.map((sf) => {
|
||||
return { n: sf.n, lvl: sf.lvl };
|
||||
});
|
||||
return [...Player.sourceFiles].map(([n, lvl]) => ({ n, lvl }));
|
||||
},
|
||||
getAugmentationsFromFaction: (ctx) => (_facName) => {
|
||||
helpers.checkSingularityAccess(ctx);
|
||||
|
@ -7,7 +7,8 @@ import { parse } from "acorn";
|
||||
|
||||
import { LoadedModule, ScriptURL, ScriptModule } from "./Script/LoadedModule";
|
||||
import { Script } from "./Script/Script";
|
||||
import { areImportsEquals, removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { ScriptFilename, scriptFilenameFromImport } from "./Types/strings";
|
||||
|
||||
// Acorn type def is straight up incomplete so we have to fill with our own.
|
||||
export type Node = any;
|
||||
@ -46,7 +47,7 @@ const cleanup = new FinalizationRegistry((mapKey: string) => {
|
||||
}
|
||||
});
|
||||
|
||||
export function compile(script: Script, scripts: Script[]): Promise<ScriptModule> {
|
||||
export function compile(script: Script, scripts: Map<ScriptFilename, Script>): Promise<ScriptModule> {
|
||||
// Return the module if it already exists
|
||||
if (script.mod) return script.mod.module;
|
||||
|
||||
@ -75,7 +76,7 @@ function addDependencyInfo(script: Script, seenStack: Script[]) {
|
||||
* @param scripts array of other scripts on the server
|
||||
* @param seenStack A stack of scripts that were higher up in the import tree in a recursive call.
|
||||
*/
|
||||
function generateLoadedModule(script: Script, scripts: Script[], seenStack: Script[]): LoadedModule {
|
||||
function generateLoadedModule(script: Script, scripts: Map<ScriptFilename, Script>, seenStack: Script[]): LoadedModule {
|
||||
// Early return for recursive calls where the script already has a URL
|
||||
if (script.mod) {
|
||||
addDependencyInfo(script, seenStack);
|
||||
@ -124,10 +125,10 @@ function generateLoadedModule(script: Script, scripts: Script[], seenStack: Scri
|
||||
let newCode = script.code;
|
||||
// Loop through each node and replace the script name with a blob url.
|
||||
for (const node of importNodes) {
|
||||
const filename = node.filename.startsWith("./") ? node.filename.substring(2) : node.filename;
|
||||
const filename = scriptFilenameFromImport(node.filename);
|
||||
|
||||
// Find the corresponding script.
|
||||
const importedScript = scripts.find((s) => areImportsEquals(s.filename, filename));
|
||||
const importedScript = scripts.get(filename);
|
||||
if (!importedScript) continue;
|
||||
|
||||
seenStack.push(script);
|
||||
|
@ -29,10 +29,10 @@ import { roundToTwo } from "./utils/helpers/roundToTwo";
|
||||
|
||||
import { parse } from "acorn";
|
||||
import { simple as walksimple } from "acorn-walk";
|
||||
import { areFilesEqual } from "./Terminal/DirectoryHelpers";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { ScriptArg } from "@nsdefs";
|
||||
import { handleUnknownError, CompleteRunOptions } from "./Netscript/NetscriptHelpers";
|
||||
import { scriptFilenameFromImport } from "./Types/strings";
|
||||
|
||||
export const NetscriptPorts: Map<PortNumber, Port> = new Map();
|
||||
|
||||
@ -147,12 +147,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): { c
|
||||
}
|
||||
|
||||
function getScript(scriptName: string): Script | null {
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === scriptName) {
|
||||
return server.scripts[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return server.scripts.get(scriptName) ?? null;
|
||||
}
|
||||
|
||||
let generatedCode = ""; // Generated Javascript Code
|
||||
@ -162,10 +157,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): { c
|
||||
walksimple(ast, {
|
||||
ImportDeclaration: (node: Node) => {
|
||||
hasImports = true;
|
||||
let scriptName = node.source.value;
|
||||
if (scriptName.startsWith("./")) {
|
||||
scriptName = scriptName.slice(2);
|
||||
}
|
||||
const scriptName = scriptFilenameFromImport(node.source.value, true);
|
||||
const script = getScript(scriptName);
|
||||
if (script == null) {
|
||||
throw new Error("'Import' failed due to invalid script: " + scriptName);
|
||||
@ -398,7 +390,7 @@ export function runScriptFromScript(
|
||||
* running a large number of scripts. */
|
||||
|
||||
// Find the script, fail if it doesn't exist.
|
||||
const script = host.scripts.find((serverScript) => areFilesEqual(serverScript.filename, scriptname));
|
||||
const script = host.scripts.get(scriptname);
|
||||
if (!script) {
|
||||
workerScript.log(caller, () => `Could not find script '${scriptname}' on '${host.hostname}'`);
|
||||
return 0;
|
||||
|
@ -8,7 +8,6 @@ import * as workMethods from "./PlayerObjectWorkMethods";
|
||||
|
||||
import { setPlayer } from "../../Player";
|
||||
import { Sleeve } from "../Sleeve/Sleeve";
|
||||
import { PlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
|
||||
import { Exploit } from "../../Exploits/Exploit";
|
||||
|
||||
import { LocationName } from "../../Enums";
|
||||
@ -21,6 +20,7 @@ import { HashManager } from "../../Hacknet/HashManager";
|
||||
|
||||
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
|
||||
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver";
|
||||
import { JSONMap } from "../../Types/Jsonable";
|
||||
import { PlayerAchievement } from "../../Achievements/Achievements";
|
||||
import { cyrb53 } from "../../utils/StringHelperFunctions";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
@ -59,7 +59,7 @@ export class PlayerObject extends Person implements IPlayer {
|
||||
scriptProdSinceLastAug = 0;
|
||||
sleeves: Sleeve[] = [];
|
||||
sleevesFromCovenant = 0;
|
||||
sourceFiles: PlayerOwnedSourceFile[] = [];
|
||||
sourceFiles: JSONMap<number, number> = new JSONMap();
|
||||
exploits: Exploit[] = [];
|
||||
achievements: PlayerAchievement[] = [];
|
||||
terminalCommandHistory: string[] = [];
|
||||
@ -168,9 +168,15 @@ export class PlayerObject extends Person implements IPlayer {
|
||||
|
||||
/** Initializes a PlayerObject object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): PlayerObject {
|
||||
if (!value.data.hp?.current || !value.data.hp?.max) value.data.hp = { current: 10, max: 10 };
|
||||
const player = Generic_fromJSON(PlayerObject, value.data);
|
||||
if (player.money === null) player.money = 0;
|
||||
player.hp = { current: player.hp?.current ?? 10, max: player.hp?.max ?? 10 };
|
||||
player.money ??= 0;
|
||||
player.updateSkillLevels();
|
||||
if (Array.isArray(player.sourceFiles)) {
|
||||
// Expect pre-2.3 sourcefile format here.
|
||||
type OldSourceFiles = { n: number; lvl: number }[];
|
||||
player.sourceFiles = new JSONMap((player.sourceFiles as OldSourceFiles).map(({ n, lvl }) => [n, lvl]));
|
||||
}
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
@ -586,14 +586,14 @@ export function reapplyAllSourceFiles(this: PlayerObject): void {
|
||||
//Will always be called after reapplyAllAugmentations() so multipliers do not have to be reset
|
||||
//this.resetMultipliers();
|
||||
|
||||
for (let i = 0; i < this.sourceFiles.length; ++i) {
|
||||
const srcFileKey = "SourceFile" + this.sourceFiles[i].n;
|
||||
for (const [bn, lvl] of this.sourceFiles) {
|
||||
const srcFileKey = "SourceFile" + bn;
|
||||
const sourceFileObject = SourceFiles[srcFileKey];
|
||||
if (sourceFileObject == null) {
|
||||
console.error(`Invalid source file number: ${this.sourceFiles[i].n}`);
|
||||
if (!sourceFileObject) {
|
||||
console.error(`Invalid source file number: ${bn}`);
|
||||
continue;
|
||||
}
|
||||
applySourceFile(this.sourceFiles[i]);
|
||||
applySourceFile(bn, lvl);
|
||||
}
|
||||
applyExploit();
|
||||
this.updateSkillLevels();
|
||||
@ -1222,9 +1222,7 @@ export function canAccessCotMG(this: PlayerObject): boolean {
|
||||
}
|
||||
|
||||
export function sourceFileLvl(this: PlayerObject, n: number): number {
|
||||
const sf = this.sourceFiles.find((sf) => sf.n === n);
|
||||
if (!sf) return 0;
|
||||
return sf.lvl;
|
||||
return this.sourceFiles.get(n) ?? 0;
|
||||
}
|
||||
|
||||
export function focusPenalty(this: PlayerObject): number {
|
||||
|
@ -10,8 +10,9 @@ export function setPlayer(playerObj: PlayerObject): void {
|
||||
Player = playerObj;
|
||||
}
|
||||
|
||||
export function loadPlayer(saveString: string): void {
|
||||
Player = JSON.parse(saveString, Reviver);
|
||||
Player.money = parseFloat(Player.money + "");
|
||||
Player.exploits = sanitizeExploits(Player.exploits);
|
||||
export function loadPlayer(saveString: string): PlayerObject {
|
||||
const player = JSON.parse(saveString, Reviver);
|
||||
player.money = parseFloat(player.money + "");
|
||||
player.exploits = sanitizeExploits(player.exploits);
|
||||
return player;
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
AddToAllServers(homeComp);
|
||||
prestigeHomeComputer(homeComp);
|
||||
// Ram usage needs to be cleared for bitnode-level resets, due to possible change in singularity cost.
|
||||
for (const script of homeComp.scripts) script.ramUsage = null;
|
||||
for (const script of homeComp.scripts.values()) script.ramUsage = null;
|
||||
|
||||
// Re-create foreign servers
|
||||
initForeignServers(Player.getHomeComputer());
|
||||
|
@ -20,7 +20,7 @@ function requireHackingLevel(lvl: number) {
|
||||
|
||||
function bitFlumeRequirements() {
|
||||
return function () {
|
||||
return Player.sourceFiles.length > 0 && Player.skills.hacking >= 1;
|
||||
return Player.sourceFiles.size > 0 && Player.skills.hacking >= 1;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
/** Implementation for what happens when you destroy a BitNode */
|
||||
import React from "react";
|
||||
import { Player } from "@player";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
@ -12,32 +11,26 @@ import { Engine } from "./engine";
|
||||
function giveSourceFile(bitNodeNumber: number): void {
|
||||
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
||||
const sourceFile = SourceFiles[sourceFileKey];
|
||||
if (sourceFile == null) {
|
||||
if (!sourceFile) {
|
||||
console.error(`Could not find source file for Bit node: ${bitNodeNumber}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player already has this source file
|
||||
const ownedSourceFile = Player.sourceFiles.find((sourceFile) => sourceFile.n === bitNodeNumber);
|
||||
let lvl = Player.sourceFileLvl(bitNodeNumber);
|
||||
|
||||
if (ownedSourceFile) {
|
||||
if (ownedSourceFile.lvl >= 3 && ownedSourceFile.n !== 12) {
|
||||
if (lvl > 0) {
|
||||
if (lvl >= 3 && bitNodeNumber !== 12) {
|
||||
dialogBoxCreate(
|
||||
`The Source-File for the BitNode you just destroyed, ${sourceFile.name}, is already at max level!`,
|
||||
);
|
||||
} else {
|
||||
++ownedSourceFile.lvl;
|
||||
dialogBoxCreate(
|
||||
sourceFile.name +
|
||||
" was upgraded to level " +
|
||||
ownedSourceFile.lvl +
|
||||
" for " +
|
||||
"destroying its corresponding BitNode!",
|
||||
);
|
||||
lvl++;
|
||||
Player.sourceFiles.set(bitNodeNumber, lvl);
|
||||
dialogBoxCreate(`${sourceFile.name} was upgraded to level ${lvl} for destroying its corresponding BitNode!`);
|
||||
}
|
||||
} else {
|
||||
const newSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
|
||||
Player.sourceFiles.push(newSrcFile);
|
||||
Player.sourceFiles.set(bitNodeNumber, 1);
|
||||
if (bitNodeNumber === 5 && Player.skills.intelligence === 0) {
|
||||
Player.skills.intelligence = 1;
|
||||
}
|
||||
|
@ -87,10 +87,7 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => void | R
|
||||
const server = GetServer(msg.params.server);
|
||||
if (!server) return error("Server hostname invalid", msg);
|
||||
|
||||
const fileNameList: string[] = [
|
||||
...server.textFiles.map((txt): string => txt.filename),
|
||||
...server.scripts.map((scr): string => scr.filename),
|
||||
];
|
||||
const fileNameList: string[] = [...server.textFiles.map((txt): string => txt.filename), ...server.scripts.keys()];
|
||||
|
||||
return new RFAMessage({ result: fileNameList, id: msg.id });
|
||||
},
|
||||
@ -105,10 +102,8 @@ export const RFARequestHandler: Record<string, (message: RFAMessage) => void | R
|
||||
...server.textFiles.map((txt): FileContent => {
|
||||
return { filename: txt.filename, content: txt.text };
|
||||
}),
|
||||
...server.scripts.map((scr): FileContent => {
|
||||
return { filename: scr.filename, content: scr.code };
|
||||
}),
|
||||
];
|
||||
for (const [filename, script] of server.scripts) fileList.push({ filename, content: script.code });
|
||||
|
||||
return new RFAMessage({ result: fileList, id: msg.id });
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ import { Companies, loadCompanies } from "./Company/Companies";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Factions, loadFactions } from "./Faction/Factions";
|
||||
import { loadAllGangs, AllGangs } from "./Gang/AllGangs";
|
||||
import { Player, loadPlayer } from "./Player";
|
||||
import { Player, setPlayer, loadPlayer } from "./Player";
|
||||
import {
|
||||
saveAllServers,
|
||||
loadAllServers,
|
||||
@ -27,7 +27,6 @@ import { AwardNFG, v1APIBreak } from "./utils/v1APIBreak";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
|
||||
import { LocationName } from "./Enums";
|
||||
import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
|
||||
import { pushGameSaved } from "./Electron";
|
||||
import { defaultMonacoTheme } from "./ScriptEditor/ui/themes";
|
||||
import { FactionNames } from "./Faction/data/FactionNames";
|
||||
@ -35,6 +34,8 @@ import { Faction } from "./Faction/Faction";
|
||||
import { safelyCreateUniqueServer } from "./Server/ServerHelpers";
|
||||
import { SpecialServers } from "./Server/data/SpecialServers";
|
||||
import { v2APIBreak } from "./utils/v2APIBreak";
|
||||
import { Script } from "./Script/Script";
|
||||
import { JSONMap } from "./Types/Jsonable";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
@ -208,7 +209,7 @@ class BitburnerSaveObject {
|
||||
base64: base64Save,
|
||||
};
|
||||
|
||||
const importedPlayer = PlayerObject.fromJSON(JSON.parse(parsedSave.data.PlayerSave));
|
||||
const importedPlayer = loadPlayer(parsedSave.data.PlayerSave);
|
||||
|
||||
const playerData: ImportPlayerData = {
|
||||
identifier: importedPlayer.identifier,
|
||||
@ -224,7 +225,7 @@ class BitburnerSaveObject {
|
||||
|
||||
bitNode: importedPlayer.bitNodeN,
|
||||
bitNodeLevel: importedPlayer.sourceFileLvl(Player.bitNodeN) + 1,
|
||||
sourceFiles: importedPlayer.sourceFiles?.reduce<number>((total, current) => (total += current.lvl), 0) ?? 0,
|
||||
sourceFiles: [...importedPlayer.sourceFiles].reduce<number>((total, [__bn, lvl]) => (total += lvl), 0),
|
||||
};
|
||||
|
||||
data.playerData = playerData;
|
||||
@ -345,7 +346,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
}
|
||||
return code;
|
||||
}
|
||||
for (const server of GetAllServers()) {
|
||||
for (const server of GetAllServers() as unknown as { scripts: Script[] }[]) {
|
||||
for (const script of server.scripts) {
|
||||
script.code = convert(script.code);
|
||||
}
|
||||
@ -663,6 +664,15 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
}
|
||||
anyPlayer.lastAugReset ??= anyPlayer.lastUpdate - anyPlayer.playtimeSinceLastAug;
|
||||
anyPlayer.lastNodeReset ??= anyPlayer.lastUpdate - anyPlayer.playtimeSinceLastBitnode;
|
||||
for (const server of GetAllServers()) {
|
||||
if (Array.isArray(server.scripts)) {
|
||||
const oldScripts = server.scripts as Script[];
|
||||
server.scripts = new JSONMap();
|
||||
for (const script of oldScripts) {
|
||||
server.scripts.set(script.filename, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,7 +683,7 @@ function loadGame(saveString: string): boolean {
|
||||
|
||||
const saveObj = JSON.parse(saveString, Reviver);
|
||||
|
||||
loadPlayer(saveObj.PlayerSave);
|
||||
setPlayer(loadPlayer(saveObj.PlayerSave));
|
||||
loadAllServers(saveObj.AllServersSave);
|
||||
loadCompanies(saveObj.CompaniesSave);
|
||||
loadFactions(saveObj.FactionsSave);
|
||||
|
@ -12,8 +12,8 @@ import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
|
||||
|
||||
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
|
||||
import { Script } from "./Script";
|
||||
import { areImportsEquals } from "../Terminal/DirectoryHelpers";
|
||||
import { Node } from "../NetscriptJSEvaluator";
|
||||
import { ScriptFilename, scriptFilenameFromImport } from "../Types/strings";
|
||||
|
||||
export interface RamUsageEntry {
|
||||
type: "ns" | "dom" | "fn" | "misc";
|
||||
@ -40,7 +40,7 @@ const memCheckGlobalKey = ".__GLOBAL__";
|
||||
* RAM usage. Also accounts for imported modules.
|
||||
* @param {Script[]} otherScripts - All other scripts on the server. Used to account for imported scripts
|
||||
* @param {string} code - The code being parsed */
|
||||
function parseOnlyRamCalculate(otherScripts: Script[], code: string): RamCalculation {
|
||||
function parseOnlyRamCalculate(otherScripts: Map<ScriptFilename, Script>, code: string, ns1?: boolean): RamCalculation {
|
||||
try {
|
||||
/**
|
||||
* Maps dependent identifiers to their dependencies.
|
||||
@ -86,16 +86,9 @@ function parseOnlyRamCalculate(otherScripts: Script[], code: string): RamCalcula
|
||||
if (nextModule === undefined) throw new Error("nextModule should not be undefined");
|
||||
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) continue;
|
||||
|
||||
let script = null;
|
||||
const fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule;
|
||||
for (const s of otherScripts) {
|
||||
if (areImportsEquals(s.filename, fn)) {
|
||||
script = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (script == null) {
|
||||
const filename = scriptFilenameFromImport(nextModule, ns1);
|
||||
const script = otherScripts.get(filename);
|
||||
if (!script) {
|
||||
return { cost: RamCalculationErrorCode.ImportError }; // No such script on the server
|
||||
}
|
||||
|
||||
@ -375,9 +368,13 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsR
|
||||
* @param {Script[]} otherScripts - All other scripts on the server.
|
||||
* Used to account for imported scripts
|
||||
*/
|
||||
export function calculateRamUsage(codeCopy: string, otherScripts: Script[]): RamCalculation {
|
||||
export function calculateRamUsage(
|
||||
codeCopy: string,
|
||||
otherScripts: Map<ScriptFilename, Script>,
|
||||
ns1?: boolean,
|
||||
): RamCalculation {
|
||||
try {
|
||||
return parseOnlyRamCalculate(otherScripts, codeCopy);
|
||||
return parseOnlyRamCalculate(otherScripts, codeCopy, ns1);
|
||||
} catch (e) {
|
||||
console.error(`Failed to parse script for RAM calculations:`);
|
||||
console.error(e);
|
||||
|
@ -10,6 +10,7 @@ import { LoadedModule, ScriptURL } from "./LoadedModule";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||
import { RamCostConstants } from "../Netscript/RamCostGenerator";
|
||||
import { ScriptFilename } from "src/Types/strings";
|
||||
|
||||
export class Script {
|
||||
code: string;
|
||||
@ -81,7 +82,7 @@ export class Script {
|
||||
}
|
||||
|
||||
/** Gets the ram usage, while also attempting to update it if it's currently null */
|
||||
getRamUsage(otherScripts: Script[]): number | null {
|
||||
getRamUsage(otherScripts: Map<ScriptFilename, Script>): number | null {
|
||||
if (this.ramUsage) return this.ramUsage;
|
||||
this.updateRamUsage(otherScripts);
|
||||
return this.ramUsage;
|
||||
@ -91,8 +92,8 @@ export class Script {
|
||||
* Calculates and updates the script's RAM usage based on its code
|
||||
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
|
||||
*/
|
||||
updateRamUsage(otherScripts: Script[]): void {
|
||||
const ramCalc = calculateRamUsage(this.code, otherScripts);
|
||||
updateRamUsage(otherScripts: Map<ScriptFilename, Script>): void {
|
||||
const ramCalc = calculateRamUsage(this.code, otherScripts, this.filename.endsWith(".script"));
|
||||
if (ramCalc.cost >= RamCostConstants.Base) {
|
||||
this.ramUsage = roundToTwo(ramCalc.cost);
|
||||
this.ramUsageEntries = ramCalc.entries as RamUsageEntry[];
|
||||
|
@ -432,19 +432,18 @@ export function Root(props: IProps): React.ReactElement {
|
||||
if (server === null) throw new Error("Server should not be null but it is.");
|
||||
if (isScriptFilename(scriptToSave.fileName)) {
|
||||
//If the current script already exists on the server, overwrite it
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (scriptToSave.fileName == server.scripts[i].filename) {
|
||||
server.scripts[i].saveScript(scriptToSave.fileName, scriptToSave.code, Player.currentServer);
|
||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||
Router.toPage(Page.Terminal);
|
||||
return;
|
||||
}
|
||||
const existingScript = server.scripts.get(scriptToSave.fileName);
|
||||
if (existingScript) {
|
||||
existingScript.saveScript(scriptToSave.fileName, scriptToSave.code, Player.currentServer);
|
||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||
Router.toPage(Page.Terminal);
|
||||
return;
|
||||
}
|
||||
|
||||
//If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(scriptToSave.fileName, scriptToSave.code, Player.currentServer);
|
||||
server.scripts.push(script);
|
||||
server.scripts.set(scriptToSave.fileName, script);
|
||||
} else if (scriptToSave.isTxt) {
|
||||
for (let i = 0; i < server.textFiles.length; ++i) {
|
||||
if (server.textFiles[i].fn === scriptToSave.fileName) {
|
||||
@ -509,19 +508,18 @@ export function Root(props: IProps): React.ReactElement {
|
||||
if (server === null) throw new Error("Server should not be null but it is.");
|
||||
if (isScriptFilename(currentScript.fileName)) {
|
||||
//If the current script already exists on the server, overwrite it
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (currentScript.fileName == server.scripts[i].filename) {
|
||||
server.scripts[i].saveScript(currentScript.fileName, currentScript.code, Player.currentServer);
|
||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||
rerender();
|
||||
return;
|
||||
}
|
||||
const existingScript = server.scripts.get(currentScript.fileName);
|
||||
if (existingScript) {
|
||||
existingScript.saveScript(currentScript.fileName, currentScript.code, Player.currentServer);
|
||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||
rerender();
|
||||
return;
|
||||
}
|
||||
|
||||
//If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(currentScript.fileName, currentScript.code, Player.currentServer);
|
||||
server.scripts.push(script);
|
||||
server.scripts.set(currentScript.fileName, script);
|
||||
} else if (currentScript.isTxt) {
|
||||
for (let i = 0; i < server.textFiles.length; ++i) {
|
||||
if (server.textFiles[i].fn === currentScript.fileName) {
|
||||
@ -683,7 +681,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
if (server === null) throw new Error(`Server '${openScript.hostname}' should not be null, but it is.`);
|
||||
const data = openScript.isTxt
|
||||
? server.textFiles.find((t) => t.filename === openScript.fileName)?.text
|
||||
: server.scripts.find((s) => s.filename === openScript.fileName)?.code;
|
||||
: server.scripts.get(openScript.fileName)?.code;
|
||||
return data ?? null;
|
||||
}
|
||||
function handleFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
|
@ -8,11 +8,11 @@ import { IMinMaxRange } from "../types";
|
||||
import { createRandomIp } from "../utils/IPAddress";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
||||
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";
|
||||
|
||||
/**
|
||||
@ -50,9 +50,7 @@ export function GetServer(s: string): BaseServer | null {
|
||||
if (server) return server;
|
||||
}
|
||||
|
||||
if (!isValidIPAddress(s)) {
|
||||
return GetServerByHostname(s);
|
||||
}
|
||||
if (!isIPAddress(s)) return GetServerByHostname(s);
|
||||
|
||||
const ipserver = GetServerByIP(s);
|
||||
if (ipserver !== undefined) {
|
||||
@ -88,8 +86,8 @@ export function ipExists(ip: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createUniqueRandomIp(): string {
|
||||
let ip: string;
|
||||
export function createUniqueRandomIp(): IPAddress {
|
||||
let ip: IPAddress;
|
||||
// Repeat generating ip, until unique one is found
|
||||
do {
|
||||
ip = createRandomIp();
|
||||
@ -117,7 +115,7 @@ export const renameServer = (hostname: string, newName: string): void => {
|
||||
interface IServerParams {
|
||||
hackDifficulty?: number;
|
||||
hostname: string;
|
||||
ip: string;
|
||||
ip: IPAddress;
|
||||
maxRam?: number;
|
||||
moneyAvailable?: number;
|
||||
numOpenPortsRequired: number;
|
||||
|
@ -11,11 +11,13 @@ import { isScriptFilename } from "../Script/isScriptFilename";
|
||||
import { createRandomIp } from "../utils/IPAddress";
|
||||
import { compareArrays } from "../utils/helpers/compareArrays";
|
||||
import { ScriptArg } from "../Netscript/ScriptArg";
|
||||
import { JSONMap } from "../Types/Jsonable";
|
||||
import { IPAddress, ScriptFilename, ServerName } from "../Types/strings";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
ip?: IPAddress;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
organizationName?: string;
|
||||
@ -42,13 +44,13 @@ export abstract class BaseServer implements IServer {
|
||||
hasAdminRights = false;
|
||||
|
||||
// Hostname. Must be unique
|
||||
hostname = "";
|
||||
hostname: ServerName = "home";
|
||||
|
||||
// Flag indicating whether HTTP Port is open
|
||||
httpPortOpen = false;
|
||||
|
||||
// IP Address. Must be unique
|
||||
ip = "";
|
||||
ip = "1.1.1.1" as IPAddress;
|
||||
|
||||
// Flag indicating whether player is currently connected to this server
|
||||
isConnectedTo = false;
|
||||
@ -73,7 +75,7 @@ export abstract class BaseServer implements IServer {
|
||||
runningScripts: RunningScript[] = [];
|
||||
|
||||
// Script files on this Server
|
||||
scripts: Script[] = [];
|
||||
scripts: JSONMap<ScriptFilename, Script> = new JSONMap();
|
||||
|
||||
// Contains the hostnames of all servers that are immediately
|
||||
// reachable from this one
|
||||
@ -157,13 +159,7 @@ export abstract class BaseServer implements IServer {
|
||||
* Script object on the server (if it exists)
|
||||
*/
|
||||
getScript(scriptName: string): Script | null {
|
||||
for (let i = 0; i < this.scripts.length; i++) {
|
||||
if (this.scripts[i].filename === scriptName) {
|
||||
return this.scripts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.scripts.get(scriptName) ?? null;
|
||||
}
|
||||
|
||||
/** Returns boolean indicating whether the given script is running on this server */
|
||||
@ -184,44 +180,44 @@ export abstract class BaseServer implements IServer {
|
||||
|
||||
/**
|
||||
* Remove a file from the server
|
||||
* @param fn {string} Name of file to be deleted
|
||||
* @param filename {string} Name of file to be deleted
|
||||
* @returns {IReturnStatus} Return status object indicating whether or not file was deleted
|
||||
*/
|
||||
removeFile(fn: string): IReturnStatus {
|
||||
if (fn.endsWith(".exe") || fn.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) {
|
||||
removeFile(filename: string): IReturnStatus {
|
||||
if (filename.endsWith(".exe") || filename.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) {
|
||||
for (let i = 0; i < this.programs.length; ++i) {
|
||||
if (this.programs[i] === fn) {
|
||||
if (this.programs[i] === filename) {
|
||||
this.programs.splice(i, 1);
|
||||
return { res: true };
|
||||
}
|
||||
}
|
||||
} else if (isScriptFilename(fn)) {
|
||||
const scriptIndex = this.scripts.findIndex((script) => script.filename === fn);
|
||||
if (scriptIndex === -1) return { res: false, msg: `script ${fn} not found.` };
|
||||
if (this.isRunning(fn)) {
|
||||
} else if (isScriptFilename(filename)) {
|
||||
const script = this.scripts.get(filename);
|
||||
if (!script) return { res: false, msg: `script ${filename} not found.` };
|
||||
if (this.isRunning(filename)) {
|
||||
return { res: false, msg: "Cannot delete a script that is currently running!" };
|
||||
}
|
||||
this.scripts[scriptIndex].invalidateModule();
|
||||
this.scripts.splice(scriptIndex, 1);
|
||||
script.invalidateModule();
|
||||
this.scripts.delete(filename);
|
||||
return { res: true };
|
||||
} else if (fn.endsWith(".lit")) {
|
||||
} else if (filename.endsWith(".lit")) {
|
||||
for (let i = 0; i < this.messages.length; ++i) {
|
||||
const f = this.messages[i];
|
||||
if (typeof f === "string" && f === fn) {
|
||||
if (typeof f === "string" && f === filename) {
|
||||
this.messages.splice(i, 1);
|
||||
return { res: true };
|
||||
}
|
||||
}
|
||||
} else if (fn.endsWith(".txt")) {
|
||||
} else if (filename.endsWith(".txt")) {
|
||||
for (let i = 0; i < this.textFiles.length; ++i) {
|
||||
if (this.textFiles[i].fn === fn) {
|
||||
if (this.textFiles[i].fn === filename) {
|
||||
this.textFiles.splice(i, 1);
|
||||
return { res: true };
|
||||
}
|
||||
}
|
||||
} else if (fn.endsWith(".cct")) {
|
||||
} else if (filename.endsWith(".cct")) {
|
||||
for (let i = 0; i < this.contracts.length; ++i) {
|
||||
if (this.contracts[i].fn === fn) {
|
||||
if (this.contracts[i].fn === filename) {
|
||||
this.contracts.splice(i, 1);
|
||||
return { res: true };
|
||||
}
|
||||
@ -266,30 +262,23 @@ export abstract class BaseServer implements IServer {
|
||||
* Write to a script file
|
||||
* Overwrites existing files. Creates new files if the script does not exist.
|
||||
*/
|
||||
writeToScriptFile(fn: string, code: string): writeResult {
|
||||
const ret = { success: false, overwritten: false };
|
||||
if (!isValidFilePath(fn) || !isScriptFilename(fn)) {
|
||||
return ret;
|
||||
writeToScriptFile(filename: string, code: string): writeResult {
|
||||
if (!isValidFilePath(filename) || !isScriptFilename(filename)) {
|
||||
return { success: false, overwritten: false };
|
||||
}
|
||||
|
||||
// Check if the script already exists, and overwrite it if it does
|
||||
for (let i = 0; i < this.scripts.length; ++i) {
|
||||
if (fn === this.scripts[i].filename) {
|
||||
const script = this.scripts[i];
|
||||
script.code = code;
|
||||
// Set ramUsage to null in order to force recalculation on next run
|
||||
script.invalidateModule();
|
||||
ret.overwritten = true;
|
||||
ret.success = true;
|
||||
return ret;
|
||||
}
|
||||
const script = this.scripts.get(filename);
|
||||
if (script) {
|
||||
script.invalidateModule();
|
||||
script.code = code;
|
||||
return { success: true, overwritten: true };
|
||||
}
|
||||
|
||||
// Otherwise, create a new script
|
||||
const newScript = new Script(fn, code, this.hostname);
|
||||
this.scripts.push(newScript);
|
||||
ret.success = true;
|
||||
return ret;
|
||||
const newScript = new Script(filename, code, this.hostname);
|
||||
this.scripts.set(filename, newScript);
|
||||
return { success: true, overwritten: false };
|
||||
}
|
||||
|
||||
// Write to a text file
|
||||
|
@ -6,12 +6,13 @@ 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 { IPAddress, ServerName } from "../Types/strings";
|
||||
|
||||
export interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hackDifficulty?: number;
|
||||
hostname: string;
|
||||
ip?: string;
|
||||
ip?: IPAddress;
|
||||
isConnectedTo?: boolean;
|
||||
maxRam?: number;
|
||||
moneyAvailable?: number;
|
||||
@ -60,7 +61,7 @@ export class Server extends BaseServer {
|
||||
|
||||
// "hacknet-node-X" hostnames are reserved for Hacknet Servers
|
||||
if (this.hostname.startsWith("hacknet-node-") || this.hostname.startsWith("hacknet-server-")) {
|
||||
this.hostname = createRandomString(10);
|
||||
this.hostname = createRandomString(10) as ServerName;
|
||||
}
|
||||
|
||||
this.purchasedByPlayer = params.purchasedByPlayer != null ? params.purchasedByPlayer : false;
|
||||
|
@ -143,10 +143,13 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
Player.factions.length > 0 ||
|
||||
Player.augmentations.length > 0 ||
|
||||
Player.queuedAugmentations.length > 0 ||
|
||||
Player.sourceFiles.length > 0;
|
||||
Player.sourceFiles.size > 0;
|
||||
|
||||
const canOpenAugmentations =
|
||||
Player.augmentations.length > 0 || Player.queuedAugmentations.length > 0 || Player.sourceFiles.length > 0;
|
||||
Player.augmentations.length > 0 ||
|
||||
Player.queuedAugmentations.length > 0 ||
|
||||
Player.sourceFiles.size > 0 ||
|
||||
Player.exploits.length > 0;
|
||||
|
||||
const canOpenSleeves = Player.sleeves.length > 0;
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
export class PlayerOwnedSourceFile {
|
||||
// Source-File level
|
||||
lvl = 1;
|
||||
|
||||
// Source-File number
|
||||
n = 1;
|
||||
|
||||
constructor(n: number, level: number) {
|
||||
this.n = n;
|
||||
this.lvl = level;
|
||||
}
|
||||
}
|
@ -1,21 +1,20 @@
|
||||
import { PlayerOwnedSourceFile } from "./PlayerOwnedSourceFile";
|
||||
import { SourceFiles } from "./SourceFiles";
|
||||
|
||||
import { Player } from "@player";
|
||||
|
||||
export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
const srcFileKey = "SourceFile" + srcFile.n;
|
||||
export function applySourceFile(bn: number, lvl: number): void {
|
||||
const srcFileKey = "SourceFile" + bn;
|
||||
const sourceFileObject = SourceFiles[srcFileKey];
|
||||
if (sourceFileObject == null) {
|
||||
console.error(`Invalid source file number: ${srcFile.n}`);
|
||||
console.error(`Invalid source file number: ${bn}`);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (srcFile.n) {
|
||||
switch (bn) {
|
||||
case 1: {
|
||||
// The Source Genesis
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 16 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -51,7 +50,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 2: {
|
||||
// Rise of the Underworld
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 24 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -63,7 +62,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 3: {
|
||||
// Corporatocracy
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 8 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -79,7 +78,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 5: {
|
||||
// Artificial Intelligence
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 8 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -94,7 +93,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 6: {
|
||||
// Bladeburner
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 8 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -111,7 +110,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 7: {
|
||||
// Bladeburner 2079
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 8 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -124,7 +123,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 8: {
|
||||
// Ghost of Wall Street
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 12 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -134,7 +133,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 9: {
|
||||
// Hacktocracy
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 12 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -154,7 +153,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
case 11: {
|
||||
// The Big Crash
|
||||
let mult = 0;
|
||||
for (let i = 0; i < srcFile.lvl; ++i) {
|
||||
for (let i = 0; i < lvl; ++i) {
|
||||
mult += 32 / Math.pow(2, i);
|
||||
}
|
||||
const incMult = 1 + mult / 100;
|
||||
@ -169,7 +168,7 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
// Grants more space on Stanek's Gift.
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid source file number: ${srcFile.n}`);
|
||||
console.error(`Invalid source file number: ${bn}`);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,8 @@ export function getSubdirectories(serv: BaseServer, dir: string): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
for (const script of serv.scripts) {
|
||||
processFile(script.filename);
|
||||
for (const scriptFilename of serv.scripts.keys()) {
|
||||
processFile(scriptFilename);
|
||||
}
|
||||
|
||||
for (const txt of serv.textFiles) {
|
||||
@ -56,7 +56,7 @@ export function getSubdirectories(serv: BaseServer, dir: string): string[] {
|
||||
export function containsFiles(server: BaseServer, dir: string): boolean {
|
||||
const dirWithTrailingSlash = dir + (dir.slice(-1) === "/" ? "" : "/");
|
||||
|
||||
return [...server.scripts.map((s) => s.filename), ...server.textFiles.map((t) => t.fn)].some((filename) =>
|
||||
return [...server.scripts.keys(), ...server.textFiles.map((t) => t.fn)].some((filename) =>
|
||||
filename.startsWith(dirWithTrailingSlash),
|
||||
);
|
||||
}
|
||||
|
@ -400,16 +400,10 @@ export class Terminal {
|
||||
}
|
||||
|
||||
getScript(filename: string): Script | null {
|
||||
const s = Player.getCurrentServer();
|
||||
const server = Player.getCurrentServer();
|
||||
const filepath = this.getFilepath(filename);
|
||||
if (!filepath) return null;
|
||||
for (const script of s.scripts) {
|
||||
if (filepath === script.filename) {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
if (filepath === null) return null;
|
||||
return server.scripts.get(filepath) ?? null;
|
||||
}
|
||||
|
||||
getTextFile(filename: string): TextFile | null {
|
||||
|
@ -7,6 +7,7 @@ import { isScriptFilename } from "../../../Script/isScriptFilename";
|
||||
import { CursorPositions } from "../../../ScriptEditor/CursorPositions";
|
||||
import { Script } from "../../../Script/Script";
|
||||
import { isEmpty } from "lodash";
|
||||
import { ScriptFilename } from "src/Types/strings";
|
||||
|
||||
interface EditorParameters {
|
||||
args: (string | number | boolean)[];
|
||||
@ -28,7 +29,7 @@ interface ISimpleScriptGlob {
|
||||
postGlob: string;
|
||||
globError: string;
|
||||
globMatches: string[];
|
||||
globAgainst: Script[];
|
||||
globAgainst: Map<ScriptFilename, Script>;
|
||||
}
|
||||
|
||||
function containsSimpleGlob(filename: string): boolean {
|
||||
@ -45,7 +46,7 @@ function detectSimpleScriptGlob({ args, server }: EditorParameters): ISimpleScri
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSimpleScriptGlob(globString: string, globDatabase: Script[]): ISimpleScriptGlob {
|
||||
function parseSimpleScriptGlob(globString: string, globDatabase: Map<ScriptFilename, Script>): ISimpleScriptGlob {
|
||||
const parsedGlob: ISimpleScriptGlob = {
|
||||
glob: globString,
|
||||
preGlob: "",
|
||||
|
@ -56,17 +56,8 @@ export function cp(args: (string | number | boolean)[], server: BaseServer): voi
|
||||
}
|
||||
|
||||
// Get the current script
|
||||
let sourceScript = null;
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (areFilesEqual(server.scripts[i].filename, src)) {
|
||||
sourceScript = server.scripts[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sourceScript == null) {
|
||||
Terminal.error("cp failed. No such script exists");
|
||||
return;
|
||||
}
|
||||
const sourceScript = server.scripts.get(src);
|
||||
if (!sourceScript) return Terminal.error("cp failed. No such script exists");
|
||||
|
||||
const sRes = server.writeToScriptFile(dst, sourceScript.code);
|
||||
if (!sRes.success) {
|
||||
|
@ -19,8 +19,8 @@ export function exportScripts(pattern: string, server: BaseServer): void {
|
||||
// In the case of script files, we pull from the server.scripts array
|
||||
if (!matchEnding || isScriptFilename(matchEnding))
|
||||
zipFiles(
|
||||
server.scripts.map((s) => s.filename),
|
||||
server.scripts.map((s) => s.code),
|
||||
[...server.scripts.keys()],
|
||||
[...server.scripts.values()].map((script) => script.code),
|
||||
);
|
||||
// In the case of text files, we pull from the server.scripts array
|
||||
if (!matchEnding || matchEnding.endsWith(".txt"))
|
||||
|
@ -106,7 +106,7 @@ export function ls(args: (string | number | boolean)[], server: BaseServer): voi
|
||||
|
||||
// Get all of the programs and scripts on the machine into one temporary array
|
||||
for (const program of server.programs) handleFn(program, allPrograms);
|
||||
for (const script of server.scripts) handleFn(script.filename, allScripts);
|
||||
for (const scriptFilename of server.scripts.keys()) handleFn(scriptFilename, allScripts);
|
||||
for (const txt of server.textFiles) handleFn(txt.fn, allTextFiles);
|
||||
for (const contract of server.contracts) handleFn(contract.fn, allContracts);
|
||||
for (const msgOrLit of server.messages) handleFn(msgOrLit, allMessages);
|
||||
|
@ -43,50 +43,43 @@ export function runScript(commandArgs: (string | number | boolean)[], server: Ba
|
||||
}
|
||||
|
||||
// Check if the script exists and if it does run it
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (server.scripts[i].filename !== scriptName) {
|
||||
continue;
|
||||
}
|
||||
// Check for admin rights and that there is enough RAM available to run
|
||||
const script = server.scripts[i];
|
||||
script.server = server.hostname;
|
||||
const singleRamUsage = script.getRamUsage(server.scripts);
|
||||
if (!singleRamUsage) return Terminal.error("Error while calculating ram usage for this script.");
|
||||
const ramUsage = singleRamUsage * numThreads;
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
const script = server.scripts.get(scriptName);
|
||||
if (!script) return Terminal.error("No such script");
|
||||
|
||||
if (!server.hasAdminRights) {
|
||||
Terminal.error("Need root access to run script");
|
||||
return;
|
||||
}
|
||||
const singleRamUsage = script.getRamUsage(server.scripts);
|
||||
if (!singleRamUsage) return Terminal.error("Error while calculating ram usage for this script.");
|
||||
const ramUsage = singleRamUsage * numThreads;
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
Terminal.error(
|
||||
"This machine does not have enough RAM to run this script" +
|
||||
(numThreads === 1 ? "" : ` with ${numThreads} threads`) +
|
||||
`. Script requires ${formatRam(ramUsage)} of RAM`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Able to run script
|
||||
const runningScript = new RunningScript(script, singleRamUsage, args);
|
||||
runningScript.threads = numThreads;
|
||||
|
||||
const success = startWorkerScript(runningScript, server);
|
||||
if (!success) {
|
||||
Terminal.error(`Failed to start script`);
|
||||
return;
|
||||
}
|
||||
|
||||
Terminal.print(
|
||||
`Running script with ${numThreads} thread(s), pid ${runningScript.pid} and args: ${JSON.stringify(args)}.`,
|
||||
);
|
||||
if (tailFlag) {
|
||||
LogBoxEvents.emit(runningScript);
|
||||
}
|
||||
if (!server.hasAdminRights) {
|
||||
Terminal.error("Need root access to run script");
|
||||
return;
|
||||
}
|
||||
|
||||
Terminal.error("No such script");
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
Terminal.error(
|
||||
"This machine does not have enough RAM to run this script" +
|
||||
(numThreads === 1 ? "" : ` with ${numThreads} threads`) +
|
||||
`. Script requires ${formatRam(ramUsage)} of RAM`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Able to run script
|
||||
const runningScript = new RunningScript(script, singleRamUsage, args);
|
||||
runningScript.threads = numThreads;
|
||||
|
||||
const success = startWorkerScript(runningScript, server);
|
||||
if (!success) {
|
||||
Terminal.error(`Failed to start script`);
|
||||
return;
|
||||
}
|
||||
|
||||
Terminal.print(
|
||||
`Running script with ${numThreads} thread(s), pid ${runningScript.pid} and args: ${JSON.stringify(args)}.`,
|
||||
);
|
||||
if (tailFlag) {
|
||||
LogBoxEvents.emit(runningScript);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -51,11 +51,8 @@ export function scp(args: (string | number | boolean)[], server: BaseServer): vo
|
||||
}
|
||||
|
||||
// Get the current script
|
||||
const sourceScript = server.scripts.find((script) => script.filename === scriptname);
|
||||
if (!sourceScript) {
|
||||
Terminal.error("scp failed. No such script exists");
|
||||
return;
|
||||
}
|
||||
const sourceScript = server.scripts.get(scriptname);
|
||||
if (!sourceScript) return Terminal.error("scp failed. No such script exists");
|
||||
|
||||
const sRes = destServer.writeToScriptFile(scriptname, sourceScript.code);
|
||||
if (!sRes.success) {
|
||||
|
@ -101,8 +101,8 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
}
|
||||
|
||||
function addAllScripts(): void {
|
||||
for (const script of currServ.scripts) {
|
||||
const res = processFilepath(script.filename);
|
||||
for (const scriptFilename of currServ.scripts.keys()) {
|
||||
const res = processFilepath(scriptFilename);
|
||||
if (res) {
|
||||
allPos.push(res);
|
||||
}
|
||||
@ -281,10 +281,7 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
// Use regex to remove any leading './', and then check if it matches against
|
||||
// the output of processFilepath or if it matches with a '/' prepended,
|
||||
// this way autocomplete works inside of directories
|
||||
const script = currServ.scripts.find((script) => {
|
||||
const fn = filename.replace(/^\.\//g, "");
|
||||
return processFilepath(script.filename) === fn || script.filename === "/" + fn;
|
||||
});
|
||||
const script = currServ.scripts.get(filename);
|
||||
if (!script) return; // Doesn't exist.
|
||||
let loadedModule;
|
||||
try {
|
||||
@ -304,7 +301,7 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
const flagFunc = Flags(flags._);
|
||||
const autocompleteData: AutocompleteData = {
|
||||
servers: GetAllServers().map((server) => server.hostname),
|
||||
scripts: currServ.scripts.map((script) => script.filename),
|
||||
scripts: [...currServ.scripts.keys()],
|
||||
txts: currServ.textFiles.map((txt) => txt.fn),
|
||||
flags: (schema: unknown) => {
|
||||
if (!Array.isArray(schema)) throw new Error("flags require an array of array");
|
||||
@ -334,8 +331,8 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
// invocation of `run`.
|
||||
if (input.startsWith("./")) {
|
||||
// All programs and scripts
|
||||
for (const script of currServ.scripts) {
|
||||
const res = processFilepath(script.filename);
|
||||
for (const scriptFilename of currServ.scripts.keys()) {
|
||||
const res = processFilepath(scriptFilename);
|
||||
if (res) {
|
||||
allPos.push(res);
|
||||
}
|
||||
|
20
src/Types/Jsonable.ts
Normal file
20
src/Types/Jsonable.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import type { IReviverValue } from "../utils/JSONReviver";
|
||||
// Jsonable versions of builtin JS class objects
|
||||
export class JSONSet<T> extends Set<T> {
|
||||
toJSON(): IReviverValue {
|
||||
return { ctor: "JSONSet", data: Array.from(this) };
|
||||
}
|
||||
static fromJSON(value: IReviverValue): JSONSet<any> {
|
||||
return new JSONSet(value.data);
|
||||
}
|
||||
}
|
||||
|
||||
export class JSONMap<K, V> extends Map<K, V> {
|
||||
toJSON(): IReviverValue {
|
||||
return { ctor: "JSONMap", data: Array.from(this) };
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): JSONMap<any, any> {
|
||||
return new JSONMap(value.data);
|
||||
}
|
||||
}
|
29
src/Types/strings.ts
Normal file
29
src/Types/strings.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Script Filename
|
||||
export type ScriptFilename = string /*& { __type: "ScriptFilename" }*/;
|
||||
/*export function isScriptFilename(value: string): value is ScriptFilename {
|
||||
// implementation
|
||||
}*/
|
||||
/*export function sanitizeScriptFilename(filename: string): ScriptFilename {
|
||||
// implementation
|
||||
}*/
|
||||
export function scriptFilenameFromImport(importPath: string, ns1?: boolean): ScriptFilename {
|
||||
if (importPath.startsWith("./")) importPath = importPath.substring(2);
|
||||
if (!ns1 && !importPath.endsWith(".js")) importPath += ".js";
|
||||
if (ns1 && !importPath.endsWith(".script")) importPath += ".script";
|
||||
return importPath as ScriptFilename;
|
||||
}
|
||||
|
||||
// Server name
|
||||
export type ServerName = string /*& { __type: "ServerName" }*/;
|
||||
/*export function isExistingServerName(value: unknown): value is ServerName {
|
||||
if (AllServers.has(value as ServerName)) return true; // For AllServers as an exported map
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// IP Address
|
||||
export type IPAddress = string /*& { __type: "IPAddress" }*/;
|
||||
export function isIPAddress(value: string): value is IPAddress {
|
||||
const regex =
|
||||
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
return regex.test(value);
|
||||
}
|
@ -255,21 +255,19 @@ const Engine: {
|
||||
const contractChancesWhileOffline = Math.floor(timeOffline / (1000 * 60 * 10));
|
||||
|
||||
// Generate coding contracts
|
||||
if (Player.sourceFiles.length > 0) {
|
||||
let numContracts = 0;
|
||||
if (contractChancesWhileOffline > 100) {
|
||||
numContracts += Math.floor(contractChancesWhileOffline * 0.25);
|
||||
}
|
||||
if (contractChancesWhileOffline > 0 && contractChancesWhileOffline <= 100) {
|
||||
for (let i = 0; i < contractChancesWhileOffline; ++i) {
|
||||
if (Math.random() <= 0.25) {
|
||||
numContracts++;
|
||||
}
|
||||
let numContracts = 0;
|
||||
if (contractChancesWhileOffline > 100) {
|
||||
numContracts += Math.floor(contractChancesWhileOffline * 0.25);
|
||||
}
|
||||
if (contractChancesWhileOffline > 0 && contractChancesWhileOffline <= 100) {
|
||||
for (let i = 0; i < contractChancesWhileOffline; ++i) {
|
||||
if (Math.random() <= 0.25) {
|
||||
numContracts++;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < numContracts; i++) {
|
||||
generateRandomContract();
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < numContracts; i++) {
|
||||
generateRandomContract();
|
||||
}
|
||||
|
||||
let offlineReputation = 0;
|
||||
|
@ -84,7 +84,7 @@ function MultiplierTable(props: MultTableProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function CurrentBitNode(): React.ReactElement {
|
||||
if (Player.sourceFiles.length > 0) {
|
||||
if (Player.sourceFiles.size > 0) {
|
||||
const index = "BitNode" + Player.bitNodeN;
|
||||
const lvl = Math.min(Player.sourceFileLvl(Player.bitNodeN) + 1, Player.bitNodeN === 12 ? Infinity : 3);
|
||||
return (
|
||||
@ -175,7 +175,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
|
||||
{convertMoneySourceTrackerToString(Player.moneySourceA)}
|
||||
</>
|
||||
);
|
||||
if (Player.sourceFiles.length !== 0) {
|
||||
if (Player.sourceFiles.size > 0) {
|
||||
content = (
|
||||
<>
|
||||
{content}
|
||||
@ -205,7 +205,7 @@ export function CharacterStats(): React.ReactElement {
|
||||
const timeRows = [
|
||||
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastAug)],
|
||||
];
|
||||
if (Player.sourceFiles.length > 0) {
|
||||
if (Player.sourceFiles.size > 0) {
|
||||
timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastBitnode)]);
|
||||
}
|
||||
timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]);
|
||||
|
@ -20,7 +20,6 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { ANSIITypography } from "./ANSIITypography";
|
||||
import { ScriptArg } from "../../Netscript/ScriptArg";
|
||||
import { useRerender } from "./hooks";
|
||||
import { areFilesEqual } from "../../Terminal/DirectoryHelpers";
|
||||
import { dialogBoxCreate } from "./DialogBox";
|
||||
|
||||
let layerCounter = 0;
|
||||
@ -222,7 +221,7 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
if (server === null) return;
|
||||
const s = findRunningScript(script.filename, script.args, server);
|
||||
if (s === null) {
|
||||
const baseScript = server.scripts.find((serverScript) => areFilesEqual(serverScript.filename, script.filename));
|
||||
const baseScript = server.scripts.get(script.filename);
|
||||
if (!baseScript) {
|
||||
return dialogBoxCreate(
|
||||
`Could not launch script. The script ${script.filename} no longer exists on the server ${server.hostname}.`,
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { IPAddress } from "src/Types/strings";
|
||||
import { getRandomByte } from "./helpers/getRandomByte";
|
||||
|
||||
/**
|
||||
* Generate a random IP address
|
||||
* Does not check to see if the IP already exists in the game
|
||||
*/
|
||||
export const createRandomIp = (): string =>
|
||||
`${getRandomByte(99)}.${getRandomByte(9)}.${getRandomByte(9)}.${getRandomByte(9)}`;
|
||||
export const createRandomIp = (): IPAddress =>
|
||||
`${getRandomByte(99)}.${getRandomByte(9)}.${getRandomByte(9)}.${getRandomByte(9)}` as IPAddress;
|
||||
|
@ -1,6 +1,11 @@
|
||||
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
|
||||
|
||||
import { ObjectValidator, validateObject } from "./Validator";
|
||||
import { JSONMap, JSONSet } from "../Types/Jsonable";
|
||||
|
||||
type JsonableClass = (new () => { toJSON: () => IReviverValue }) & {
|
||||
fromJSON: (value: IReviverValue) => any;
|
||||
validationData?: ObjectValidator<any>;
|
||||
};
|
||||
|
||||
export interface IReviverValue {
|
||||
ctor: string;
|
||||
@ -38,15 +43,7 @@ export function Reviver(_key: string, value: unknown): any {
|
||||
return obj;
|
||||
}
|
||||
|
||||
export const constructorsForReviver: Partial<
|
||||
Record<
|
||||
string,
|
||||
(new () => object) & {
|
||||
fromJSON: (value: IReviverValue) => any;
|
||||
validationData?: ObjectValidator<any>;
|
||||
}
|
||||
>
|
||||
> = {};
|
||||
export const constructorsForReviver: Partial<Record<string, JsonableClass>> = { JSONSet, JSONMap };
|
||||
|
||||
/**
|
||||
* A generic "toJSON" function that creates the data expected by Reviver.
|
||||
|
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Checks whether a IP Address string is valid.
|
||||
* @param ipaddress A string representing a potential IP Address
|
||||
*/
|
||||
export function isValidIPAddress(ipaddress: string): boolean {
|
||||
const byteRange = "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
|
||||
const regexStr = `^${byteRange}\.${byteRange}\.${byteRange}\.${byteRange}$`;
|
||||
const ipAddressRegex = new RegExp(regexStr);
|
||||
|
||||
return ipAddressRegex.test(ipaddress);
|
||||
}
|
@ -95,7 +95,11 @@ export function v1APIBreak(): void {
|
||||
for (const server of GetAllServers()) {
|
||||
for (const change of detect) {
|
||||
const s: IFileLine[] = [];
|
||||
for (const script of server.scripts) {
|
||||
const scriptsArray: Script[] = Array.isArray(server.scripts)
|
||||
? (server.scripts as Script[])
|
||||
: [...server.scripts.values()];
|
||||
|
||||
for (const script of scriptsArray) {
|
||||
const lines = script.code.split("\n");
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].includes(change[0])) {
|
||||
@ -121,7 +125,8 @@ export function v1APIBreak(): void {
|
||||
home.writeToTextFile("v1_DETECTED_CHANGES.txt", txt);
|
||||
}
|
||||
|
||||
for (const server of GetAllServers()) {
|
||||
// API break function is called before version31 / 2.3.0 changes - scripts is still an array
|
||||
for (const server of GetAllServers() as unknown as { scripts: Script[] }[]) {
|
||||
const backups: Script[] = [];
|
||||
for (const script of server.scripts) {
|
||||
if (!hasChanges(script.code)) continue;
|
||||
|
@ -227,7 +227,8 @@ export const v2APIBreak = () => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const script of home.scripts) {
|
||||
// API break function is called before the version31 2.3.0 changes, scripts are still an array.
|
||||
for (const script of home.scripts as unknown as Script[]) {
|
||||
processScript(rules, script);
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,8 @@ function isRemovedFunction(ctx: NetscriptContext, fn: (ctx: NetscriptContext) =>
|
||||
}
|
||||
|
||||
describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
Player.sourceFiles[0] = { n: 4, lvl: 3 };
|
||||
Player.sourceFiles.set(4, 3);
|
||||
// For simulating costs of singularity functions.
|
||||
const sf4 = Player.sourceFiles[0];
|
||||
const baseCost = RamCostConstants.Base;
|
||||
const maxCost = RamCostConstants.Max;
|
||||
const script = new Script();
|
||||
@ -38,7 +37,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
script.code = code;
|
||||
// Force ram calculation reset
|
||||
script.ramUsage = null;
|
||||
const ramUsage = script.getRamUsage([]);
|
||||
const ramUsage = script.getRamUsage(new Map());
|
||||
if (!ramUsage) throw new Error("Ram usage should be defined.");
|
||||
const runningScript = new RunningScript(script, ramUsage);
|
||||
return runningScript;
|
||||
@ -63,7 +62,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
ramUsage: scriptRef.ramUsage,
|
||||
scriptRef,
|
||||
};
|
||||
const nsExternal = NetscriptFunctions(workerScript as WorkerScript);
|
||||
const nsExternal = NetscriptFunctions(workerScript as unknown as WorkerScript);
|
||||
|
||||
function combinedRamCheck(
|
||||
fn: PotentiallyAsyncFunction,
|
||||
@ -78,7 +77,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
expect(getRamCost(...fnPath)).toEqual(expectedRamCost);
|
||||
|
||||
// Static ram check
|
||||
const staticCost = calculateRamUsage(code, []).cost;
|
||||
const staticCost = calculateRamUsage(code, new Map()).cost;
|
||||
expect(staticCost).toBeCloseTo(Math.min(baseCost + expectedRamCost + extraLayerCost, maxCost));
|
||||
|
||||
// reset workerScript for dynamic check
|
||||
@ -144,7 +143,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
|
||||
describe("Singularity multiplier checks", () => {
|
||||
// Checks were already done above for SF4.3 having normal ramcost.
|
||||
sf4.lvl = 3;
|
||||
Player.sourceFiles.set(4, 3);
|
||||
const lvlToMult = { 0: 16, 1: 16, 2: 4 };
|
||||
const externalSingularity = nsExternal.singularity;
|
||||
const ramCostSingularity = RamCosts.singularity;
|
||||
@ -160,7 +159,7 @@ describe("Netscript RAM Calculation/Generation Tests", function () {
|
||||
});
|
||||
for (const lvl of [0, 1, 2] as const) {
|
||||
it(`SF4.${lvl} check for x${lvlToMult[lvl]} costs`, () => {
|
||||
sf4.lvl = lvl;
|
||||
Player.sourceFiles.set(4, lvl);
|
||||
const expectedMult = lvlToMult[lvl];
|
||||
singObjects.forEach(({ name, baseRam }) => {
|
||||
const fn = getFunction(externalSingularity[name]);
|
||||
|
@ -5,6 +5,7 @@ import { RunningScript } from "../../../src/Script/RunningScript";
|
||||
import { AddToAllServers, DeleteServer } from "../../../src/Server/AllServers";
|
||||
import { WorkerScriptStartStopEventEmitter } from "../../../src/Netscript/WorkerScriptStartStopEventEmitter";
|
||||
import { AlertEvents } from "../../../src/ui/React/AlertManager";
|
||||
import type { Script } from "src/Script/Script";
|
||||
|
||||
// Replace Blob/ObjectURL functions, because they don't work natively in Jest
|
||||
global.Blob = class extends Blob {
|
||||
@ -89,10 +90,11 @@ test.each([
|
||||
expect(server.writeToScriptFile(s.name, s.code)).toEqual({ success: true, overwritten: false });
|
||||
}
|
||||
|
||||
const script = server.scripts[server.scripts.length - 1];
|
||||
const script = server.scripts.get(scripts[scripts.length - 1].name) as Script;
|
||||
expect(script.filename).toEqual(scripts[scripts.length - 1].name);
|
||||
|
||||
const ramUsage = script.getRamUsage(server.scripts);
|
||||
if (!ramUsage) throw new Error(`ramUsage calculated to be ${ramUsage}`);
|
||||
const runningScript = new RunningScript(script, ramUsage as number);
|
||||
expect(startWorkerScript(runningScript, server)).toBeGreaterThan(0);
|
||||
// We don't care about start, so subscribe after that. Await script death.
|
||||
|
@ -21,7 +21,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
const code = `
|
||||
export async function main(ns) { }
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
|
||||
@ -31,7 +31,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
ns.print("Slum snakes r00l!");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
|
||||
@ -41,7 +41,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await ns.hack("joesguns");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -51,7 +51,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await X.hack("joesguns");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -62,7 +62,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await ns.hack("joesguns");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -73,7 +73,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await ns.grow("joesguns");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost + GrowCost);
|
||||
});
|
||||
|
||||
@ -86,7 +86,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await ns.hack("joesguns");
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -101,7 +101,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
async doHacking() { await this.ns.hack("joesguns"); }
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -116,7 +116,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
async doHacking() { await this.#ns.hack("joesguns"); }
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
});
|
||||
@ -129,7 +129,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
}
|
||||
function get() { return 0; }
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
|
||||
@ -140,7 +140,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
}
|
||||
function purchaseNode() { return 0; }
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
// Works at present, because the parser checks the namespace only, not the function name
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
@ -153,7 +153,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
}
|
||||
function getTask() { return 0; }
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
});
|
||||
@ -165,7 +165,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
ns.hacknet.purchaseNode(0);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, HacknetCost);
|
||||
});
|
||||
|
||||
@ -175,7 +175,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
ns.corporation.getCorporation();
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, MaxCost);
|
||||
});
|
||||
|
||||
@ -186,7 +186,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
ns.corporation.getCorporation();
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, MaxCost);
|
||||
});
|
||||
|
||||
@ -196,7 +196,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
ns.sleeve.getTask(3);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, []).cost;
|
||||
const calculated = calculateRamUsage(code, new Map()).cost;
|
||||
expectCost(calculated, SleeveGetTaskCost);
|
||||
});
|
||||
});
|
||||
@ -214,7 +214,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
dummy();
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, 0);
|
||||
});
|
||||
|
||||
@ -230,7 +230,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await doHack(ns);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -247,7 +247,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await doHack(ns);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -264,7 +264,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await test.doHack(ns);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, HackCost + GrowCost);
|
||||
});
|
||||
|
||||
@ -286,7 +286,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await test.doHack(ns);
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, HackCost);
|
||||
});
|
||||
|
||||
@ -312,7 +312,7 @@ describe("Parsing NetScript code to work out static RAM costs", function () {
|
||||
await growerInstance.doGrow();
|
||||
}
|
||||
`;
|
||||
const calculated = calculateRamUsage(code, [lib]).cost;
|
||||
const calculated = calculateRamUsage(code, new Map([["libTest.js", lib]])).cost;
|
||||
expectCost(calculated, GrowCost);
|
||||
});
|
||||
});
|
||||
|
@ -89,7 +89,10 @@ exports[`load/saveAllServers 1`] = `
|
||||
"programs": [],
|
||||
"ramUsed": 0,
|
||||
"runningScripts": [],
|
||||
"scripts": [],
|
||||
"scripts": {
|
||||
"ctor": "JSONMap",
|
||||
"data": []
|
||||
},
|
||||
"serversOnNetwork": [
|
||||
"home"
|
||||
],
|
||||
@ -183,7 +186,10 @@ exports[`load/saveAllServers pruning RunningScripts 1`] = `
|
||||
"programs": [],
|
||||
"ramUsed": 0,
|
||||
"runningScripts": [],
|
||||
"scripts": [],
|
||||
"scripts": {
|
||||
"ctor": "JSONMap",
|
||||
"data": []
|
||||
},
|
||||
"serversOnNetwork": [
|
||||
"home"
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user