diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 1d27fbbf7..623d50fac 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -1326,12 +1326,6 @@ export const ns: InternalAPI = { }, writePort: (ctx) => (_portNumber, data) => { const portNumber = helpers.portNumber(ctx, _portNumber); - if (typeof data !== "string" && typeof data !== "number") { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Trying to write invalid data to a port: only strings and numbers are valid.`, - ); - } return writePort(portNumber, data); }, write: (ctx) => (_filename, _data, _mode) => { @@ -1364,12 +1358,6 @@ export const ns: InternalAPI = { }, tryWritePort: (ctx) => (_portNumber, data) => { const portNumber = helpers.portNumber(ctx, _portNumber); - if (typeof data !== "string" && typeof data !== "number") { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Trying to write invalid data to a port: only strings and numbers are valid.`, - ); - } return tryWritePort(portNumber, data); }, nextPortWrite: (ctx) => (_portNumber) => { diff --git a/src/NetscriptPort.ts b/src/NetscriptPort.ts index 4ceb44ffd..f9dc1dcb1 100644 --- a/src/NetscriptPort.ts +++ b/src/NetscriptPort.ts @@ -3,12 +3,15 @@ import { NetscriptPort } from "@nsdefs"; import { NetscriptPorts } from "./NetscriptWorker"; import { PositiveInteger } from "./types"; -type PortData = string | number; type Resolver = () => void; const emptyPortData = "NULL PORT DATA"; /** The object property is for typechecking and is not present at runtime */ export type PortNumber = PositiveInteger & { __PortNumber: true }; +function isObjectLike(value: unknown): value is object { + return (typeof value === "object" && value !== null) || typeof value === "function"; +} + /** Gets the numbered port, initializing it if it doesn't already exist. * Only using for functions that write data/resolvers. Use NetscriptPorts.get(n) for */ export function getPort(n: PortNumber) { @@ -20,10 +23,11 @@ export function getPort(n: PortNumber) { } export class Port { - data: PortData[] = []; + data: any[] = []; resolver: Resolver | null = null; promise: Promise | null = null; - resolve() { + add(data: any) { + this.data.push(data); if (!this.resolver) return; this.resolver(); this.resolver = null; @@ -43,44 +47,35 @@ export function portHandle(n: PortNumber): NetscriptPort { }; } -export function writePort(n: PortNumber, value: unknown): PortData | null { - if (typeof value !== "number" && typeof value !== "string") { - throw new Error( - `port.write: Tried to write type ${typeof value}. Only string and number types may be written to ports.`, - ); - } +export function writePort(n: PortNumber, value: unknown): any { const port = getPort(n); - port.data.push(value); - port.resolve(); - if (port.data.length > Settings.MaxPortCapacity) return port.data.shift() as PortData; + // Primitives don't need to be cloned. + port.add(isObjectLike(value) ? structuredClone(value) : value); + if (port.data.length > Settings.MaxPortCapacity) return port.data.shift(); return null; } export function tryWritePort(n: PortNumber, value: unknown): boolean { - if (typeof value != "number" && typeof value != "string") { - throw new Error( - `port.write: Tried to write type ${typeof value}. Only string and number types may be written to ports.`, - ); - } const port = getPort(n); if (port.data.length >= Settings.MaxPortCapacity) return false; - port.data.push(value); - port.resolve(); + // Primitives don't need to be cloned. + port.add(isObjectLike(value) ? structuredClone(value) : value); return true; } -export function readPort(n: PortNumber): PortData { +export function readPort(n: PortNumber): any { const port = NetscriptPorts.get(n); if (!port || !port.data.length) return emptyPortData; - const returnVal = port.data.shift() as PortData; + const returnVal = port.data.shift(); if (!port.data.length && !port.resolver) NetscriptPorts.delete(n); return returnVal; } -export function peekPort(n: PortNumber): PortData { +export function peekPort(n: PortNumber): any { const port = NetscriptPorts.get(n); if (!port || !port.data.length) return emptyPortData; - return port.data[0]; + // Needed to avoid exposing internal objects. + return isObjectLike(port.data[0]) ? structuredClone(port.data[0]) : port.data[0]; } export function nextPortWrite(n: PortNumber) { diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 2585a22cf..44be5d702 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -24,9 +24,6 @@ interface Skills { */ type CodingContractData = any; -/** @public */ -type PortData = string | number; - /** @public */ type ScriptArg = string | number | boolean; @@ -1054,20 +1051,23 @@ export interface NetscriptPort { * @remarks * RAM cost: 0 GB * - * @returns The data popped off the queue if it was full. */ - write(value: string | number): PortData | null; + * @param value - Data to write, it's cloned with structuredClone(). + * @returns The data popped off the queue if it was full. + */ + write(value: any): any; /** * Attempt to write data to the port. * @remarks * RAM cost: 0 GB * + * @param value - Data to write, it's cloned with structuredClone(). * @returns True if the data was added to the port, false if the port was full */ - tryWrite(value: string | number): boolean; + tryWrite(value: any): boolean; /** - * Sleeps until the port is written to. + * Waits until the port is written to. * @remarks * RAM cost: 0 GB */ @@ -1082,7 +1082,7 @@ export interface NetscriptPort { * If the port is empty, then the string “NULL PORT DATA” will be returned. * @returns the data read. */ - read(): PortData; + read(): any; /** * Retrieve the first element from the port without removing it. @@ -1094,7 +1094,7 @@ export interface NetscriptPort { * the port is empty, the string “NULL PORT DATA” will be returned. * @returns the data read */ - peek(): PortData; + peek(): any; /** * Check if the port is full. @@ -6642,10 +6642,10 @@ export interface NS { * Otherwise, the data will be written normally. * * @param portNumber - Port to attempt to write to. Must be a positive integer. - * @param data - Data to write. + * @param data - Data to write, it's cloned with structuredClone(). * @returns True if the data is successfully written to the port, and false otherwise. */ - tryWritePort(portNumber: number, data: string | number): boolean; + tryWritePort(portNumber: number, data: any): boolean; /** * Listen for a port write. @@ -6685,7 +6685,7 @@ export interface NS { * @param portNumber - Port to peek. Must be a positive integer. * @returns Data in the specified port. */ - peek(portNumber: number): PortData; + peek(portNumber: number): any; /** * Clear data from a file. @@ -6716,10 +6716,10 @@ export interface NS { * * Write data to the given Netscript port. * @param portNumber - Port to write to. Must be a positive integer. - * @param data - Data to write. + * @param data - Data to write, it's cloned with structuredClone(). * @returns The data popped off the queue if it was full, or null if it was not full. */ - writePort(portNumber: number, data: string | number): PortData | null; + writePort(portNumber: number, data: any): any; /** * Read data from a port. * @remarks @@ -6731,7 +6731,7 @@ export interface NS { * @param portNumber - Port to read from. Must be a positive integer. * @returns The data read. */ - readPort(portNumber: number): PortData; + readPort(portNumber: number): any; /** * Get all data on a port.