From 74e2acfa6fc57fd148cdff6b73a14b6534a20c01 Mon Sep 17 00:00:00 2001 From: smolgumball Date: Wed, 19 Jan 2022 00:04:48 -0700 Subject: [PATCH 1/2] Add `ns.getRecentScripts()` --- src/Netscript/RamCostGenerator.ts | 1 + src/Netscript/RecentScripts.ts | 11 ++- src/NetscriptFunctions.ts | 49 +++++++---- src/ScriptEditor/NetscriptDefinitions.d.ts | 56 ++++++++++++- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 83 ++++++++++++------- src/Settings/Settings.ts | 7 ++ .../ActiveScripts/RecentScriptAccordion.tsx | 2 +- src/ui/React/GameOptionsRoot.tsx | 26 ++++++ 8 files changed, 184 insertions(+), 51 deletions(-) diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 81625b6c5..c16fd38a4 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -135,6 +135,7 @@ export const RamCosts: IMap = { scp: RamCostConstants.ScriptScpRamCost, ls: RamCostConstants.ScriptScanRamCost, ps: RamCostConstants.ScriptScanRamCost, + getRecentScripts: RamCostConstants.ScriptScanRamCost, hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost, getIp: RamCostConstants.ScriptGetHostnameRamCost, getHostname: RamCostConstants.ScriptGetHostnameRamCost, diff --git a/src/Netscript/RecentScripts.ts b/src/Netscript/RecentScripts.ts index 02e584b0d..d2f4ef04a 100644 --- a/src/Netscript/RecentScripts.ts +++ b/src/Netscript/RecentScripts.ts @@ -1,19 +1,23 @@ import { RunningScript } from "src/Script/RunningScript"; +import { Settings } from "../Settings/Settings"; import { WorkerScript } from "./WorkerScript"; export const recentScripts: RecentScript[] = []; export function AddRecentScript(workerScript: WorkerScript): void { if (recentScripts.find((r) => r.pid === workerScript.pid)) return; + + const killedTime = new Date(); recentScripts.unshift({ filename: workerScript.name, args: workerScript.args, pid: workerScript.pid, - timestamp: new Date(), - + timestamp: killedTime, + timestampEpoch: killedTime.getTime(), runningScript: workerScript.scriptRef, }); - while (recentScripts.length > 50) { + + while (recentScripts.length > Settings.MaxRecentScriptsCapacity) { recentScripts.pop(); } } @@ -23,5 +27,6 @@ export interface RecentScript { args: string[]; pid: number; timestamp: Date; + timestampEpoch: number; runningScript: RunningScript; } diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 5d43cd1e9..b7870f50f 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -78,6 +78,8 @@ import { Gang as IGang, Bladeburner as IBladeburner, Stanek as IStanek, + RunningScript as IRunningScript, + RecentScript as IRecentScript, SourceFileLvl, } from "./ScriptEditor/NetscriptDefinitions"; import { NetscriptSingularity } from "./NetscriptFunctions/Singularity"; @@ -90,6 +92,7 @@ import { SnackbarEvents } from "./ui/React/Snackbar"; import { Flags } from "./NetscriptFunctions/Flags"; import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence"; import { CalculateShareMult, StartSharing } from "./NetworkShare/Share"; +import { recentScripts } from "./Netscript/RecentScripts"; interface NS extends INS { [key: string]: any; @@ -204,6 +207,32 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { return null; }; + /** + * Sanitizes a `RunningScript` to remove sensitive information, making it suitable for + * return through an NS function. + * @see NS.getRecentScripts + * @see NS.getRunningScript + * @param runningScript Existing, internal RunningScript + * @returns A sanitized, NS-facing copy of the RunningScript + */ + const createPublicRunningScript = function (runningScript: RunningScript): IRunningScript { + return { + args: runningScript.args.slice(), + filename: runningScript.filename, + logs: runningScript.logs.slice(), + offlineExpGained: runningScript.offlineExpGained, + offlineMoneyMade: runningScript.offlineMoneyMade, + offlineRunningTime: runningScript.offlineRunningTime, + onlineExpGained: runningScript.onlineExpGained, + onlineMoneyMade: runningScript.onlineMoneyMade, + onlineRunningTime: runningScript.onlineRunningTime, + pid: runningScript.pid, + ramUsage: runningScript.ramUsage, + server: runningScript.server, + threads: runningScript.threads, + }; + }; + /** * Helper function for getting the error log message when the user specifies * a nonexistent running script @@ -1310,6 +1339,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { allFiles.sort(); return allFiles; }, + getRecentScripts: function (): IRecentScript[] { + updateDynamicRam("getRecentScripts", getRamCost(Player, "getRecentScripts")); + return recentScripts.map((rs) => ({ ...rs, runningScript: createPublicRunningScript(rs.runningScript) })); + }, ps: function (hostname: any = workerScript.hostname): any { updateDynamicRam("ps", getRamCost(Player, "ps")); const server = safeGetServer(hostname, "ps"); @@ -1974,21 +2007,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { runningScript = getRunningScript(fn, hostname, "getRunningScript", args); } if (runningScript === null) return null; - return { - args: runningScript.args.slice(), - filename: runningScript.filename, - logs: runningScript.logs.slice(), - offlineExpGained: runningScript.offlineExpGained, - offlineMoneyMade: runningScript.offlineMoneyMade, - offlineRunningTime: runningScript.offlineRunningTime, - onlineExpGained: runningScript.onlineExpGained, - onlineMoneyMade: runningScript.onlineMoneyMade, - onlineRunningTime: runningScript.onlineRunningTime, - pid: runningScript.pid, - ramUsage: runningScript.ramUsage, - server: runningScript.server, - threads: runningScript.threads, - }; + return createPublicRunningScript(runningScript); }, getHackTime: function (hostname: any): any { updateDynamicRam("getHackTime", getRamCost(Player, "getHackTime")); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 6021a88ef..724789894 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -99,22 +99,56 @@ interface Player { /** * @public */ -interface RunningScript { +export interface RunningScript { + /** Arguments the script was called with */ args: string[]; + /** Filename of the script */ filename: string; + /** + * Script logs as an array. The newest log entries are at the bottom. + * Timestamps, if enabled, are placed inside `[brackets]` at the start of each line. + **/ logs: string[]; + /** Total amount of hacking experience earned from this script when offline */ offlineExpGained: number; + /** Total amount of money made by this script when offline */ offlineMoneyMade: number; + /** Number of seconds that the script has been running offline */ offlineRunningTime: number; + /** Total amount of hacking experience earned from this script when online */ onlineExpGained: number; + /** Total amount of money made by this script when online */ onlineMoneyMade: number; + /** Number of seconds that this script has been running online */ onlineRunningTime: number; + /** Process ID. Must be an integer */ pid: number; + /** How much RAM this script uses for ONE thread */ ramUsage: number; + /** Hostname of the server on which this script runs */ server: string; + /** Number of threads that this script runs with */ threads: number; } +/** + * @public + */ +interface RecentScript { + /** Arguments the script was called with */ + args: string[]; + /** Filename of the script */ + filename: string; + /** Process ID. Must be an integer */ + pid: number; + /** Timestamp of when the script was killed */ + timestamp: Date; + /** Numeric epoch of timestamp */ + timestampEpoch: number; + /** An inactive copy of the last `RunningScript` associated to the script */ + runningScript: RunningScript; +} + /** * Data representing the internal values of a crime. * @public @@ -4567,6 +4601,26 @@ export interface NS extends Singularity { */ getScriptLogs(fn?: string, host?: string, ...args: any[]): string[]; + /** + * Get an array of recently killed scripts across all servers. + * @remarks + * RAM cost: 0.2 GB + * + * The most recently killed script is the first element in the array. + * Note that there is a maximum number of recently killed scripts which are tracked. + * This is configurable in the game's options as `Recently killed scripts size`. + * + * @usage below: + * ```ts + * let recentScripts = ns.getRecentScripts(); + * let mostRcent = recentScripts.pop() + * ns.tprint(mostRecent.logs.join('\n')) + * ``` + * + * @returns Array with information about previously killed scripts. + */ + getRecentScripts(): RecentScript[]; + /** * Open the tail window of a script. * @remarks diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index c21681b4c..fc4d998ce 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -131,13 +131,12 @@ export function Root(props: IProps): React.ReactElement { // Prevent Crash if script is open on deleted server openScripts = openScripts.filter((script) => { return GetServer(script.hostname) !== null; - }) - if (currentScript && (GetServer(currentScript.hostname) === null)) { + }); + if (currentScript && GetServer(currentScript.hostname) === null) { currentScript = openScripts[0]; if (currentScript === undefined) currentScript = null; } - const [dimensions, setDimensions] = useState({ height: window.innerHeight, width: window.innerWidth, @@ -205,7 +204,7 @@ export function Root(props: IProps): React.ReactElement { }); editor.focus(); }); - } catch { } + } catch {} } else if (!options.vim) { // Whem vim mode is disabled vimEditor?.dispose(); @@ -314,13 +313,18 @@ export function Root(props: IProps): React.ReactElement { .loader(); // replaced the bare tokens with regexes surrounded by \b, e.g. \b{token}\b which matches a word-break on either side // this prevents the highlighter from highlighting pieces of variables that start with a reserved token name - l.language.tokenizer.root.unshift([new RegExp('\\bns\\b'), { token: "ns" }]); - for (const symbol of symbols) l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]); + l.language.tokenizer.root.unshift([new RegExp("\\bns\\b"), { token: "ns" }]); + for (const symbol of symbols) + l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]); const otherKeywords = ["let", "const", "var", "function"]; const otherKeyvars = ["true", "false", "null", "undefined"]; - otherKeywords.forEach((k) => l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }])); - otherKeyvars.forEach((k) => l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }])); - l.language.tokenizer.root.unshift([new RegExp('\\bthis\\b'), { token: "this" }]); + otherKeywords.forEach((k) => + l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]), + ); + otherKeyvars.forEach((k) => + l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]), + ); + l.language.tokenizer.root.unshift([new RegExp("\\bthis\\b"), { token: "this" }]); })(); const source = (libSource + "").replace(/export /g, ""); @@ -446,7 +450,7 @@ export function Root(props: IProps): React.ReactElement { } try { infLoop(newCode); - } catch (err) { } + } catch (err) {} } function saveScript(scriptToSave: OpenScript): void { @@ -755,13 +759,16 @@ export function Root(props: IProps): React.ReactElement { @@ -770,13 +777,15 @@ export function Root(props: IProps): React.ReactElement { style={{ maxWidth: "20px", minWidth: "20px", - ...(currentScript?.fileName === openScripts[index].fileName ? { - background: Settings.theme.button, - color: Settings.theme.primary - } : { - background: Settings.theme.backgroundsecondary, - color: Settings.theme.secondary - }) + ...(currentScript?.fileName === openScripts[index].fileName + ? { + background: Settings.theme.button, + color: Settings.theme.primary, + } + : { + background: Settings.theme.backgroundsecondary, + color: Settings.theme.secondary, + }), }} > x @@ -813,20 +822,30 @@ export function Root(props: IProps): React.ReactElement { > - + - - + {" "} - Documentation:{" "} + Documentation:{" "} Basic - {" "} - | + + {" | "} Full @@ -858,7 +877,9 @@ export function Root(props: IProps): React.ReactElement { {n} - {r} + + {r} + ))} diff --git a/src/Settings/Settings.ts b/src/Settings/Settings.ts index ddb18d8c1..085a4a691 100644 --- a/src/Settings/Settings.ts +++ b/src/Settings/Settings.ts @@ -63,6 +63,11 @@ interface IDefaultSettings { */ Locale: string; + /** + * Limit the number of recently killed script entries being tracked. + */ + MaxRecentScriptsCapacity: number; + /** * Limit the number of log entries for each script being executed on each server. */ @@ -186,6 +191,7 @@ export const defaultSettings: IDefaultSettings = { EnableBashHotkeys: false, TimestampsFormat: "", Locale: "en", + MaxRecentScriptsCapacity: 50, MaxLogCapacity: 50, MaxPortCapacity: 50, MaxTerminalCapacity: 500, @@ -222,6 +228,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = { EnableBashHotkeys: defaultSettings.EnableBashHotkeys, TimestampsFormat: defaultSettings.TimestampsFormat, Locale: "en", + MaxRecentScriptsCapacity: defaultSettings.MaxRecentScriptsCapacity, MaxLogCapacity: defaultSettings.MaxLogCapacity, MaxPortCapacity: defaultSettings.MaxPortCapacity, MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity, diff --git a/src/ui/ActiveScripts/RecentScriptAccordion.tsx b/src/ui/ActiveScripts/RecentScriptAccordion.tsx index d91d33dfe..49e687540 100644 --- a/src/ui/ActiveScripts/RecentScriptAccordion.tsx +++ b/src/ui/ActiveScripts/RecentScriptAccordion.tsx @@ -26,7 +26,7 @@ import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFuncti import { arrayToString } from "../../utils/helpers/arrayToString"; import { Money } from "../React/Money"; import { MoneyRate } from "../React/MoneyRate"; -import { RecentScript } from "../..//Netscript/RecentScripts"; +import { RecentScript } from "../../Netscript/RecentScripts"; import { LogBoxEvents } from "../React/LogBoxManager"; const useStyles = makeStyles({ diff --git a/src/ui/React/GameOptionsRoot.tsx b/src/ui/React/GameOptionsRoot.tsx index e1ce714a6..2c866ff40 100644 --- a/src/ui/React/GameOptionsRoot.tsx +++ b/src/ui/React/GameOptionsRoot.tsx @@ -67,6 +67,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { const importInput = useRef(null); const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime); + const [recentScriptsSize, setRecentScriptsSize] = useState(Settings.MaxRecentScriptsCapacity); const [logSize, setLogSize] = useState(Settings.MaxLogCapacity); const [portSize, setPortSize] = useState(Settings.MaxPortCapacity); const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity); @@ -84,6 +85,11 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { Settings.CodeInstructionRunTime = newValue as number; } + function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void { + setRecentScriptsSize(newValue as number); + Settings.MaxRecentScriptsCapacity = newValue as number; + } + function handleLogSizeChange(event: any, newValue: number | number[]): void { setLogSize(newValue as number); Settings.MaxLogCapacity = newValue as number; @@ -224,6 +230,26 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { valueLabelDisplay="auto" /> + + + The maximum number of recently killed script entries being tracked. Setting this too high can cause + the game to use a lot of memory. + + } + > + Recently killed scripts size + + + Date: Wed, 19 Jan 2022 13:32:07 -0700 Subject: [PATCH 2/2] Cleanup @usage docs --- src/ScriptEditor/NetscriptDefinitions.d.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 724789894..bb3cb08c5 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -4613,8 +4613,9 @@ export interface NS extends Singularity { * @usage below: * ```ts * let recentScripts = ns.getRecentScripts(); - * let mostRcent = recentScripts.pop() - * ns.tprint(mostRecent.logs.join('\n')) + * let mostRecent = recentScripts.shift() + * if (mostRecent) + * ns.tprint(mostRecent.logs.join('\n')) * ``` * * @returns Array with information about previously killed scripts.