mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-22 14:12:27 +01:00
SETTINGS: Add an autoexec setting (#505)
This commit is contained in:
parent
4e07900c5a
commit
e2e9b084bc
106
src/GameOptions/ui/AutoexecInput.tsx
Normal file
106
src/GameOptions/ui/AutoexecInput.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import React, { useState } from "react";
|
||||
import { Box, InputAdornment, TextField, Tooltip, Typography } from "@mui/material";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { parseCommand } from "../../Terminal/Parser";
|
||||
import { resolveScriptFilePath } from "../../Paths/ScriptFilePath";
|
||||
import { formatRam } from "../../ui/formatNumber";
|
||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||
import WarningIcon from "@mui/icons-material/Warning";
|
||||
import ErrorIcon from "@mui/icons-material/Error";
|
||||
import { Player } from "@player";
|
||||
|
||||
interface IProps {
|
||||
tooltip: React.ReactElement;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const AutoexecInput = (props: IProps): React.ReactElement => {
|
||||
const [autoexec, setAutoexec] = useState(Settings.AutoexecScript);
|
||||
|
||||
function handleAutoexecChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
Settings.AutoexecScript = event.target.value;
|
||||
setAutoexec(event.target.value);
|
||||
}
|
||||
|
||||
// None of these errors block the saving of the setting; what is invalid now
|
||||
// could become valid later.
|
||||
function createTooltip() {
|
||||
const args = parseCommand(autoexec);
|
||||
if (args.length === 0) {
|
||||
return (
|
||||
<Tooltip title={<Typography>No script will be auto-launched</Typography>}>
|
||||
<CheckCircleIcon color="primary" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
const cmd = String(args[0]);
|
||||
const scriptPath = resolveScriptFilePath(cmd);
|
||||
if (!scriptPath) {
|
||||
return (
|
||||
<Tooltip title={<Typography>"{cmd}" is invalid for a script name (maybe missing suffix?)</Typography>}>
|
||||
<ErrorIcon color="error" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
const home = Player.getHomeComputer();
|
||||
const script = home.scripts.get(scriptPath);
|
||||
if (!script) {
|
||||
return (
|
||||
<Tooltip title={<Typography>{cmd} does not exist!</Typography>}>
|
||||
<ErrorIcon color="error" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
const ramUsage = script.getRamUsage(home.scripts);
|
||||
if (ramUsage === null) {
|
||||
return (
|
||||
<Tooltip title={<Typography>{cmd} has errors!</Typography>}>
|
||||
<ErrorIcon color="error" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
// Stolen from Prestige.ts
|
||||
const minRam = Player.sourceFileLvl(9) >= 2 ? 128 : Player.sourceFileLvl(1) > 0 ? 32 : 8;
|
||||
if (ramUsage <= minRam) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
{cmd} costs {formatRam(ramUsage)}
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<CheckCircleIcon color="primary" />
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
{cmd} costs {formatRam(ramUsage)}, you might only have {formatRam(minRam)} on home!
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<WarningIcon color="warning" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Tooltip title={<Typography>{props.tooltip}</Typography>}>
|
||||
<Typography>{props.label}</Typography>
|
||||
</Tooltip>
|
||||
<TextField
|
||||
fullWidth
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end">{createTooltip()}</InputAdornment>,
|
||||
}}
|
||||
value={autoexec}
|
||||
onChange={handleAutoexecChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -2,6 +2,7 @@ import React, { useState } from "react";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { GameOptionsPage } from "./GameOptionsPage";
|
||||
import { OptionsSlider } from "./OptionsSlider";
|
||||
import { AutoexecInput } from "./AutoexecInput";
|
||||
import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||
|
||||
export const SystemPage = (): React.ReactElement => {
|
||||
@ -45,6 +46,16 @@ export const SystemPage = (): React.ReactElement => {
|
||||
<GameOptionsPage title="System">
|
||||
{/* Wrap in a React fragment to prevent the sliders from breaking as list items */}
|
||||
<>
|
||||
<AutoexecInput
|
||||
label="Autoexec Script + Args"
|
||||
tooltip={
|
||||
<>
|
||||
Path to a script (with optional args) to run on game load. The script will be run on home, launched before
|
||||
any saved running scripts. It will have the "temporary" setting, so if it stays running it won't be saved.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<OptionsSlider
|
||||
label=".script exec time (ms)"
|
||||
initialValue={execTime}
|
||||
@ -148,7 +159,8 @@ export const SystemPage = (): React.ReactElement => {
|
||||
tooltip={
|
||||
<>
|
||||
If this is set, the save file will exclude all running scripts. This is only useful if your save is lagging
|
||||
a lot. You'll have to restart your script every time you launch the game.
|
||||
a lot. You'll have to restart your script every time you launch the game, possibly by using the "autoexec"
|
||||
option.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
@ -23,11 +23,13 @@ import { Settings } from "./Settings/Settings";
|
||||
import { generate } from "escodegen";
|
||||
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
import { formatRam } from "./ui/formatNumber";
|
||||
import { arrayToString } from "./utils/helpers/ArrayHelpers";
|
||||
import { roundToTwo } from "./utils/helpers/roundToTwo";
|
||||
|
||||
import { parse } from "acorn";
|
||||
import { simple as walksimple } from "acorn-walk";
|
||||
import { parseCommand } from "./Terminal/Parser";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { ScriptArg } from "@nsdefs";
|
||||
import { handleUnknownError, CompleteRunOptions, getRunningScriptsByArgs } from "./Netscript/NetscriptHelpers";
|
||||
@ -284,10 +286,12 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
// Check failure conditions before generating the workersScript and return false
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
dialogBoxCreate(
|
||||
`Not enough RAM to run script ${runningScriptObj.filename} with args ${arrayToString(runningScriptObj.args)}.\n` +
|
||||
`This can occur when you reload the game and the script's RAM usage has increased (either because of an update to the game or ` +
|
||||
`your changes to the script).\nThis can also occur if you have attempted to launch a script from a tail window with insufficient RAM. `,
|
||||
deferredError(
|
||||
`Not enough RAM to run script ${runningScriptObj.filename} with args ${arrayToString(
|
||||
runningScriptObj.args,
|
||||
)}, needed ${formatRam(ramUsage)} but only have ${formatRam(ramAvailable)} free
|
||||
If you are seeing this on startup, likely causes are that the autoexec script is too big to fit in RAM, or it took up too much space and other previously running scripts couldn't fit on home.
|
||||
Otherwise, this can also occur if you have attempted to launch a script from a tail window with insufficient RAM.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@ -295,7 +299,7 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
// Get the pid
|
||||
const pid = generateNextPid();
|
||||
if (pid === -1) {
|
||||
dialogBoxCreate(
|
||||
deferredError(
|
||||
`Failed to start script because could not find available PID. This is most ` +
|
||||
`because you have too many scripts running.`,
|
||||
);
|
||||
@ -340,6 +344,42 @@ export function updateOnlineScriptTimes(numCycles = 1): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Needed for popping dialog boxes in functions that run *before* the UI is
|
||||
// created, and thus before AlertManager exists to listen to the alerts we
|
||||
// create.
|
||||
function deferredError(msg: string) {
|
||||
setTimeout(() => dialogBoxCreate(msg), 0);
|
||||
}
|
||||
|
||||
function createAutoexec(server: BaseServer): RunningScript | null {
|
||||
const args = parseCommand(Settings.AutoexecScript);
|
||||
if (args.length === 0) return null;
|
||||
|
||||
const cmd = String(args[0]);
|
||||
const scriptPath = resolveScriptFilePath(cmd);
|
||||
if (!scriptPath) {
|
||||
deferredError(`While running autoexec script:
|
||||
"${cmd}" is invalid for a script name (maybe missing suffix?)`);
|
||||
return null;
|
||||
}
|
||||
const script = server.scripts.get(scriptPath);
|
||||
if (!script) {
|
||||
deferredError(`While running autoexec script:
|
||||
"${cmd}" does not exist!`);
|
||||
return null;
|
||||
}
|
||||
const ramUsage = script.getRamUsage(server.scripts);
|
||||
if (ramUsage === null) {
|
||||
deferredError(`While running autoexec script:
|
||||
"${cmd}" has errors!`);
|
||||
return null;
|
||||
}
|
||||
args.shift();
|
||||
const rs = new RunningScript(script, ramUsage, args);
|
||||
rs.temporary = true;
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the game is loaded. Loads all running scripts (from all servers)
|
||||
* into worker scripts so that they will start running
|
||||
@ -360,6 +400,13 @@ export function loadAllRunningScripts(): void {
|
||||
// Start game with no scripts
|
||||
continue;
|
||||
}
|
||||
if (server.hostname === "home") {
|
||||
// Push autoexec script onto the front of the list
|
||||
const runningScript = createAutoexec(server);
|
||||
if (runningScript) {
|
||||
rsList.unshift(runningScript);
|
||||
}
|
||||
}
|
||||
for (const runningScript of rsList) {
|
||||
startWorkerScript(runningScript, server);
|
||||
scriptCalculateOfflineProduction(runningScript);
|
||||
|
@ -10,6 +10,8 @@ export const Settings = {
|
||||
ActiveScriptsServerPageSize: 10,
|
||||
/** How many scripts per page */
|
||||
ActiveScriptsScriptPageSize: 10,
|
||||
/** Script + args to launch on game load */
|
||||
AutoexecScript: "",
|
||||
/** How often the game should autosave the player's progress, in seconds. */
|
||||
AutosaveInterval: 60,
|
||||
/** How many milliseconds between execution points for Netscript 1 statements. */
|
||||
|
Loading…
Reference in New Issue
Block a user