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.
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.
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 |
| --- | --- | --- | --- |
| [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
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:**

@ -93,7 +93,7 @@ export interface CompleteRunOptions {
}
/** SpawnOptions with non-optional, type-validated members, for passing between internal functions. */
export interface CompleteSpawnOptions extends CompleteRunOptions {
spawnDelay: PositiveInteger;
spawnDelay: number;
}
/** HGWOptions with non-optional, type-validated members, for passing between internal functions. */
export interface CompleteHGWOptions {
@ -189,11 +189,16 @@ function runOptions(ctx: NetscriptContext, threadOrOption: unknown): CompleteRun
}
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;
// Safe assertion since threadOrOption type has been narrowed to a non-null object
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;
}

@ -737,23 +737,36 @@ export const ns: InternalAPI<NSFull> = {
const path = helpers.scriptPath(ctx, "scriptname", _scriptname);
const runOpts = helpers.spawnOptions(ctx, _thread_or_opt);
const args = helpers.scriptArgs(ctx, _args);
setTimeout(() => {
const spawnCb = () => {
if (Router.page() === Page.BitVerse) {
helpers.log(ctx, () => `Script execution is canceled because you are in Bitverse.`);
return;
}
const scriptServer = GetServer(ctx.workerScript.hostname);
if (scriptServer === null) {
if (scriptServer == null) {
throw helpers.errorMessage(ctx, `Cannot find server ${ctx.workerScript.hostname}`);
}
runScriptFromScript("spawn", scriptServer, path, args, ctx.workerScript, runOpts);
}, runOpts.spawnDelay);
return runScriptFromScript("spawn", scriptServer, path, args, ctx.workerScript, runOpts);
};
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, () => "Exiting...");
helpers.log(ctx, () => "About to exit...");
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:

@ -283,7 +283,10 @@ interface RunOptions {
/** @public */
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;
}
@ -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
* 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.
*
* Running this function with 0 or fewer threads will cause a runtime error.