MISC: Make spawn able to have 0 delay (#1333)

This eliminates a hole where spawn was unrelaible, because other scripts
could jump in and steal the RAM. It's not an API break, because 0 used
to be an invalid value.
This commit is contained in:
David Walker 2024-06-28 18:41:41 -07:00 committed by GitHub
parent 06d742a7f3
commit 1c20a24079
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 39 additions and 13 deletions

@ -30,6 +30,8 @@ RAM cost: 2 GB
Terminates the current script, and then after a defined delay it will execute the newly-specified script. The purpose of this function is to execute a new script without being constrained by the RAM usage of the current one. This function can only be used to run scripts on the local server. Terminates the current script, and then after a defined delay it will execute the newly-specified script. The purpose of this function is to execute a new script without being constrained by the RAM usage of the current one. This function can only be used to run scripts on the local server.
The delay specified can be 0; in this case the new script will synchronously replace the old one. (There will not be any opportunity for other scripts to use up the RAM in-between.)
Because this function immediately terminates the script, it does not have a return value. Because this function immediately terminates the script, it does not have a return value.
Running this function with 0 or fewer threads will cause a runtime error. Running this function with 0 or fewer threads will cause a runtime error.

@ -16,5 +16,5 @@ interface SpawnOptions extends RunOptions
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer. | | [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously. |

@ -4,7 +4,7 @@
## SpawnOptions.spawnDelay property ## SpawnOptions.spawnDelay property
Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer. Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously.
**Signature:** **Signature:**

@ -93,7 +93,7 @@ export interface CompleteRunOptions {
} }
/** SpawnOptions with non-optional, type-validated members, for passing between internal functions. */ /** SpawnOptions with non-optional, type-validated members, for passing between internal functions. */
export interface CompleteSpawnOptions extends CompleteRunOptions { export interface CompleteSpawnOptions extends CompleteRunOptions {
spawnDelay: PositiveInteger; spawnDelay: number;
} }
/** HGWOptions with non-optional, type-validated members, for passing between internal functions. */ /** HGWOptions with non-optional, type-validated members, for passing between internal functions. */
export interface CompleteHGWOptions { export interface CompleteHGWOptions {
@ -189,11 +189,16 @@ function runOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteRun
} }
function spawnOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteSpawnOptions { function spawnOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteSpawnOptions {
const result: CompleteSpawnOptions = { spawnDelay: 10000 as PositiveInteger, ...runOptions(ctx, threadOrOption) }; const result: CompleteSpawnOptions = { spawnDelay: 10000, ...runOptions(ctx, threadOrOption) };
if (typeof threadOrOption !== "object" || !threadOrOption) return result; if (typeof threadOrOption !== "object" || !threadOrOption) return result;
// Safe assertion since threadOrOption type has been narrowed to a non-null object // Safe assertion since threadOrOption type has been narrowed to a non-null object
const { spawnDelay } = threadOrOption as Unknownify<CompleteSpawnOptions>; const { spawnDelay } = threadOrOption as Unknownify<CompleteSpawnOptions>;
if (spawnDelay !== undefined) result.spawnDelay = positiveInteger(ctx, "spawnDelayMsec", spawnDelay); if (spawnDelay !== undefined) {
result.spawnDelay = number(ctx, "spawnDelay", spawnDelay);
if (result.spawnDelay < 0) {
throw errorMessage(ctx, `spawnDelay must be non-negative, got ${spawnDelay}`);
}
}
return result; return result;
} }

@ -737,23 +737,36 @@ export const ns: InternalAPI<NSFull> = {
const path = helpers.scriptPath(ctx, "scriptname", _scriptname); const path = helpers.scriptPath(ctx, "scriptname", _scriptname);
const runOpts = helpers.spawnOptions(ctx, _thread_or_opt); const runOpts = helpers.spawnOptions(ctx, _thread_or_opt);
const args = helpers.scriptArgs(ctx, _args); const args = helpers.scriptArgs(ctx, _args);
setTimeout(() => { const spawnCb = () => {
if (Router.page() === Page.BitVerse) { if (Router.page() === Page.BitVerse) {
helpers.log(ctx, () => `Script execution is canceled because you are in Bitverse.`); helpers.log(ctx, () => `Script execution is canceled because you are in Bitverse.`);
return; return;
} }
const scriptServer = GetServer(ctx.workerScript.hostname); const scriptServer = GetServer(ctx.workerScript.hostname);
if (scriptServer === null) { if (scriptServer == null) {
throw helpers.errorMessage(ctx, `Cannot find server ${ctx.workerScript.hostname}`); throw helpers.errorMessage(ctx, `Cannot find server ${ctx.workerScript.hostname}`);
} }
runScriptFromScript("spawn", scriptServer, path, args, ctx.workerScript, runOpts); return runScriptFromScript("spawn", scriptServer, path, args, ctx.workerScript, runOpts);
}, runOpts.spawnDelay); };
helpers.log(ctx, () => `Will execute '${path}' in ${runOpts.spawnDelay} milliseconds`); if (runOpts.spawnDelay !== 0) {
setTimeout(spawnCb, runOpts.spawnDelay);
helpers.log(ctx, () => `Will execute '${path}' in ${runOpts.spawnDelay} milliseconds`);
}
if (killWorkerScript(ctx.workerScript)) { helpers.log(ctx, () => "About to exit...");
helpers.log(ctx, () => "Exiting..."); const killed = killWorkerScript(ctx.workerScript);
if (runOpts.spawnDelay === 0) {
helpers.log(ctx, () => `Executing '${path}' immediately`);
spawnCb();
}
if (killed) {
// This prevents error messages about statements after the spawn()
// trying to be executed when the script is dead.
throw new ScriptDeath(ctx.workerScript);
} }
}, },
kill: kill:

@ -283,7 +283,10 @@ interface RunOptions {
/** @public */ /** @public */
interface SpawnOptions extends RunOptions { interface SpawnOptions extends RunOptions {
/** Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer. */ /**
* Number of milliseconds to delay before spawning script, defaults to 10000 (10s).
* Must be a non-negative integer. If 0, the script will be spawned synchronously.
*/
spawnDelay?: number; spawnDelay?: number;
} }
@ -6261,6 +6264,9 @@ export interface NS {
* constrained by the RAM usage of the current one. This function can only be used to run scripts * constrained by the RAM usage of the current one. This function can only be used to run scripts
* on the local server. * on the local server.
* *
* The delay specified can be 0; in this case the new script will synchronously replace
* the old one. (There will not be any opportunity for other scripts to use up the RAM in-between.)
*
* Because this function immediately terminates the script, it does not have a return value. * Because this function immediately terminates the script, it does not have a return value.
* *
* Running this function with 0 or fewer threads will cause a runtime error. * Running this function with 0 or fewer threads will cause a runtime error.