This commit is contained in:
Olivier Gagnon 2022-01-15 17:53:19 -05:00
commit 58fa091dd5
11 changed files with 316 additions and 102 deletions

@ -113,7 +113,7 @@ The list contains the name of (i.e. the value returned by
| | | to any position from i to i+n. | | | | to any position from i to i+n. |
| | | | | | | |
| | | Assuming you are initially positioned at the start of the array, determine | | | | Assuming you are initially positioned at the start of the array, determine |
| | | whether you are able to reach the last index of the array EXACTLY. | | | | whether you are able to reach the last index of the array. |
+------------------------------------+------------------------------------------------------------------------------------------+ +------------------------------------+------------------------------------------------------------------------------------------+
| Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval | | Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval |
| | | is an array with two numbers, where the first number is always less than | | | | is an array with two numbers, where the first number is always less than |

@ -17,9 +17,16 @@ says 'Infiltrate Company'.
When infiltrating a company you will be presented with short active challenges. When infiltrating a company you will be presented with short active challenges.
None of the challenges use the mouse. None of the challenges use the mouse.
The difficulty at the top lowers with better combat stats. It is not recommended The difficulty at the top lowers with better combat stats and charisma. It is not recommended
to attempt infiltrations above mid-normal. to attempt infiltrations above mid-normal.
The "maximum level" is the number of challenges you will need to pass to receive
the infiltration reward.
Every time you fail an infiltration challenge, you will take damage based on the
difficulty of the infiltration. If you are reduced to 0 hp or below, the
infiltration will immediately end.
* Most use spacebar as "action" * Most use spacebar as "action"
* Some use WASD or arrows interchangeably. * Some use WASD or arrows interchangeably.
* A few others use the rest of the keyboard. * A few others use the rest of the keyboard.
@ -60,4 +67,4 @@ Then move the cursor and press space to mark the mines on the board.
** Cut the wires ** ** Cut the wires **
Follow the instructions and press the numbers 1 through 9 to cut the appropriate Follow the instructions and press the numbers 1 through 9 to cut the appropriate
wires. wires.

@ -16,7 +16,6 @@ Affects:
* Chance to successfully hack a server * Chance to successfully hack a server
* Percent money stolen when hacking a server * Percent money stolen when hacking a server
* Success rate of certain crimes * Success rate of certain crimes
* Success rate of Hacking option during Infiltration
* Time it takes to create a program * Time it takes to create a program
* Faction reputation gain when carrying out Hacking Contracts or Field Work * Faction reputation gain when carrying out Hacking Contracts or Field Work
* Company reputation gain for certain jobs * Company reputation gain for certain jobs
@ -26,7 +25,6 @@ Gain experience by:
* Manually hacking servers through Terminal * Manually hacking servers through Terminal
* Executing hack(), grow(), or weaken() through a script * Executing hack(), grow(), or weaken() through a script
* Committing certain crimes * Committing certain crimes
* Infiltration
* Carrying out Hacking Contracts or doing Field work for Factions * Carrying out Hacking Contracts or doing Field work for Factions
* Working certain jobs at a company * Working certain jobs at a company
* Studying at a university * Studying at a university
@ -38,14 +36,12 @@ Represents the player's physical offensive power
Affects: Affects:
* Success rate of certain crimes * Success rate of certain crimes
* Success rate of Combat options during Infiltration
* Faction reputation gain for Security and Field Work * Faction reputation gain for Security and Field Work
* Company reputation gain for certain jobs * Company reputation gain for certain jobs
Gain experience by: Gain experience by:
* Committing certain crimes * Committing certain crimes
* Infiltration
* Working out at a gym * Working out at a gym
* Doing Security/Field Work for a faction * Doing Security/Field Work for a faction
* Working certain jobs at a company * Working certain jobs at a company
@ -58,15 +54,12 @@ Affects:
* Success rate of certain crimes * Success rate of certain crimes
* The player's HP * The player's HP
* Success rate of Combat options during Infiltration
* How much damage the player takes during Infiltration
* Faction reputation gain for Security and Field Work * Faction reputation gain for Security and Field Work
* Company reputation gain for certain jobs * Company reputation gain for certain jobs
Gain experience by: Gain experience by:
* Committing certain crimes * Committing certain crimes
* Infiltration
* Working out at a gym * Working out at a gym
* Doing Security/Field Work for a faction * Doing Security/Field Work for a faction
* Working certain jobs at a company * Working certain jobs at a company
@ -78,14 +71,12 @@ Represents the player's skill and adeptness in performing certain tasks
Affects: Affects:
* Success rate of certain crimes * Success rate of certain crimes
* Success rate of Combat, Lockpick, and Escape options during Infiltration
* Faction reputation gain for Security and Field Work * Faction reputation gain for Security and Field Work
* Company reputation gain for certain jobs * Company reputation gain for certain jobs
Gain experience by: Gain experience by:
* Committing certain crimes * Committing certain crimes
* Infiltration
* Working out at a gym * Working out at a gym
* Doing Security/Field Work for a faction * Doing Security/Field Work for a faction
* Working certain jobs at a company * Working certain jobs at a company
@ -97,14 +88,12 @@ Represents the player's speed and ability to move
Affects: Affects:
* Success rate of certain crimes * Success rate of certain crimes
* Success rate of Combat, Sneak, and Escape options during Infiltration
* Faction reputation gain for Security and Field Work * Faction reputation gain for Security and Field Work
* Company reputation gain for certain jobs * Company reputation gain for certain jobs
Gain experience by: Gain experience by:
* Committing certain crimes * Committing certain crimes
* Infiltration
* Working out at a gym * Working out at a gym
* Doing Security/Field Work for a faction * Doing Security/Field Work for a faction
* Working certain jobs at a company * Working certain jobs at a company
@ -116,14 +105,12 @@ Represents the player's social abilities
Affects: Affects:
* Success rate of certain crimes * Success rate of certain crimes
* Success rate of Bribe option during Infiltration
* Faction reputation gain for Field Work * Faction reputation gain for Field Work
* Company reputation gain for most jobs * Company reputation gain for most jobs
Gain experience by: Gain experience by:
* Committing certain crimes * Committing certain crimes
* Infiltration
* Studying at a university * Studying at a university
* Working a relevant job at a company * Working a relevant job at a company
* Doing Field work for a Faction * Doing Field work for a Faction

@ -200,52 +200,52 @@ Here's what mine showed at the time I made this::
--Root Access: YES, Required hacking skill: 1 --Root Access: YES, Required hacking skill: 1
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 4.00GB --RAM: 4.00GB
----zer0 ----zer0
------Root Access: NO, Required hacking skill: 75 ------Root Access: NO, Required hacking skill: 75
------Number of open ports required to NUKE: 1 ------Number of open ports required to NUKE: 1
------RAM: 32.00GB ------RAM: 32.00GB
foodnstuff foodnstuff
--Root Access: NO, Required hacking skill: 1 --Root Access: NO, Required hacking skill: 1
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 16.00GB --RAM: 16.00GB
sigma-cosmetics sigma-cosmetics
--Root Access: NO, Required hacking skill: 5 --Root Access: NO, Required hacking skill: 5
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 16.00GB --RAM: 16.00GB
joesguns joesguns
--Root Access: NO, Required hacking skill: 10 --Root Access: NO, Required hacking skill: 10
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 16.00GB --RAM: 16.00GB
----max-hardware ----max-hardware
------Root Access: NO, Required hacking skill: 80 ------Root Access: NO, Required hacking skill: 80
------Number of open ports required to NUKE: 1 ------Number of open ports required to NUKE: 1
------RAM: 32.00GB ------RAM: 32.00GB
----CSEC ----CSEC
------Root Access: NO, Required hacking skill: 54 ------Root Access: NO, Required hacking skill: 54
------Number of open ports required to NUKE: 1 ------Number of open ports required to NUKE: 1
------RAM: 8.00GB ------RAM: 8.00GB
hong-fang-tea hong-fang-tea
--Root Access: NO, Required hacking skill: 30 --Root Access: NO, Required hacking skill: 30
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 16.00GB --RAM: 16.00GB
----nectar-net ----nectar-net
------Root Access: NO, Required hacking skill: 20 ------Root Access: NO, Required hacking skill: 20
------Number of open ports required to NUKE: 0 ------Number of open ports required to NUKE: 0
------RAM: 16.00GB ------RAM: 16.00GB
harakiri-sushi harakiri-sushi
--Root Access: NO, Required hacking skill: 40 --Root Access: NO, Required hacking skill: 40
--Number of open ports required to NUKE: 0 --Number of open ports required to NUKE: 0
--RAM: 16.00GB --RAM: 16.00GB
iron-gym iron-gym
--Root Access: NO, Required hacking skill: 100 --Root Access: NO, Required hacking skill: 100
--Number of open ports required to NUKE: 1 --Number of open ports required to NUKE: 1
@ -806,8 +806,7 @@ startup script. Feel free to adjust it to your liking.
// Array of all servers that don't need any ports opened // Array of all servers that don't need any ports opened
// to gain root access. These have 16 GB of RAM // to gain root access. These have 16 GB of RAM
var servers0Port = ["n00dles", var servers0Port = ["sigma-cosmetics",
"sigma-cosmetics",
"joesguns", "joesguns",
"nectar-net", "nectar-net",
"hong-fang-tea", "hong-fang-tea",

@ -59,15 +59,14 @@ And the data in port 1 will look like::
.. warning:: In :ref:`netscriptjs`, do not trying writing base .. warning:: In :ref:`netscriptjs`, do not trying writing base
`Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ `Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_
to a port. to a port.
**Port Handles** **Port Handles**
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1` WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port. The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
This handle allows you to access several new port-related functions and the This handle allows you to access several new port-related functions. The functions are:
port's underlying data structure, which is just a JavaScript array. The functions are:
.. js:method:: NetscriptPort.writePort(data) .. js:method:: NetscriptPort.writePort(data)
@ -111,22 +110,11 @@ port's underlying data structure, which is just a JavaScript array. The function
Clears all data from the port. Works the same as the Netscript function `clear` Clears all data from the port. Works the same as the Netscript function `clear`
.. js:attribute:: NetscriptPort.data
The Netscript port underlying data structure, which is just a Javascript array. All
valid Javascript Array methods can be called on this.
Port Handle Example:: Port Handle Example::
port = getPortHandle(5); port = getPortHandle(5);
back = port.data.pop(); //Get and remove last element in port back = port.data.pop(); //Get and remove last element in port
//Remove an element from the port
i = port.data.findIndex("foo");
if (i != -1) {
port.data.slice(i, 1);
}
//Wait for port data before reading //Wait for port data before reading
while(port.empty()) { while(port.empty()) {
sleep(10000); sleep(10000);

@ -18,8 +18,18 @@ import { HacknetNodeConstants } from "./data/Constants";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { ObjectValidator, minMax } from "../utils/Validator";
export class HacknetNode implements IHacknetNode { export class HacknetNode implements IHacknetNode {
static validationData: ObjectValidator<HacknetNode> = {
cores: minMax(1, 1, HacknetNodeConstants.MaxCores),
level: minMax(1, 1, HacknetNodeConstants.MaxLevel),
ram: minMax(1, 1, HacknetNodeConstants.MaxRam),
onlineTimeSeconds: minMax(0, 0, Infinity),
totalMoneyGenerated: minMax(0, 0, Infinity)
}
// Node's number of cores // Node's number of cores
cores = 1; cores = 1;

@ -109,32 +109,6 @@ interface RunningScript {
threads: number; threads: number;
} }
/**
* Interface of a netscript port
* @public
*/
export interface IPort {
/** write data to the port and removes and returns first element if full */
write: (value: any) => any;
/** add data to port if not full.
* @returns true if added and false if full and not added */
tryWrite: (value: any) => boolean;
/** reads and removes first element from port
* if no data in port returns "NULL PORT DATA"
*/
read: () => any;
/** reads first element without removing it from port
* if no data in port returns "NULL PORT DATA"
*/
peek: () => any;
/** check if port is full */
full: () => boolean;
/** check if port is empty */
empty: () => boolean;
/** removes all data from port */
clear: () => void;
}
/** /**
* Data representing the internal values of a crime. * Data representing the internal values of a crime.
* @public * @public
@ -280,6 +254,24 @@ export interface AugmentPair {
cost: number; cost: number;
} }
/**
* @public
*/
export enum PositionTypes {
Long = "L",
Short = "S",
}
/**
* @public
*/
export enum OrderTypes {
LimitBuy = "Limit Buy Order",
LimitSell = "Limit Sell Order",
StopBuy = "Stop Buy Order",
StopSell = "Stop Sell Order",
}
/** /**
* Value in map of {@link StockOrder} * Value in map of {@link StockOrder}
* @public * @public
@ -290,17 +282,18 @@ export interface StockOrderObject {
/** Price per share */ /** Price per share */
price: number; price: number;
/** Order type */ /** Order type */
type: string; type: OrderTypes;
/** Order position */ /** Order position */
position: string; position: PositionTypes;
} }
/** /**
* Return value of {@link TIX.getOrders | getOrders} * Return value of {@link TIX.getOrders | getOrders}
*
* Keys are stock symbols, properties are arrays of {@link StockOrderObject}
* @public * @public
*/ */
export interface StockOrder { export interface StockOrder {
/** Stock Symbol */
[key: string]: StockOrderObject[]; [key: string]: StockOrderObject[];
} }
@ -488,6 +481,8 @@ export interface BitNodeMultipliers {
FourSigmaMarketDataApiCost: number; FourSigmaMarketDataApiCost: number;
/** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */ /** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */
FourSigmaMarketDataCost: number; FourSigmaMarketDataCost: number;
/** Influences the respect gain and money gain of your gang. */
GangSoftcap: number;
/** Influences the experienced gained when hacking a server. */ /** Influences the experienced gained when hacking a server. */
HackExpGain: number; HackExpGain: number;
/** Influences how quickly the player's hacking level (not experience) scales */ /** Influences how quickly the player's hacking level (not experience) scales */
@ -508,10 +503,14 @@ export interface BitNodeMultipliers {
PurchasedServerLimit: number; PurchasedServerLimit: number;
/** Influences the maximum allowed RAM for a purchased server */ /** Influences the maximum allowed RAM for a purchased server */
PurchasedServerMaxRam: number; PurchasedServerMaxRam: number;
/** Influences cost of any purchased server at or above 128GB */
PurchasedServerSoftCap: number;
/** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */ /** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */
RepToDonateToFaction: number; RepToDonateToFaction: number;
/** Influences how much money can be stolen from a server when a script performs a hack against it. */ /** Influences how much the money on a server can be reduced when a script performs a hack against it. */
ScriptHackMoney: number; ScriptHackMoney: number;
/** Influences how much of the money stolen by a scripted hack will be added to the player's money. */
ScriptHackMoneyGain: number;
/** Influences the growth percentage per cycle against a server. */ /** Influences the growth percentage per cycle against a server. */
ServerGrowthRate: number; ServerGrowthRate: number;
/** Influences the maxmimum money that a server can grow to. */ /** Influences the maxmimum money that a server can grow to. */
@ -524,6 +523,12 @@ export interface BitNodeMultipliers {
ServerWeakenRate: number; ServerWeakenRate: number;
/** Influences how quickly the player's strength level (not exp) scales */ /** Influences how quickly the player's strength level (not exp) scales */
StrengthLevelMultiplier: number; StrengthLevelMultiplier: number;
/** Influences the power of the gift */
StaneksGiftPowerMultiplier: number;
/** Influences the size of the gift */
StaneksGiftExtraSize: number;
/** Influences the hacking skill required to backdoor the world daemon. */
WorldDaemonDifficulty: number;
} }
/** /**
@ -535,9 +540,9 @@ export interface NodeStats {
name: string; name: string;
/** Node's level */ /** Node's level */
level: number; level: number;
/** Node's RAM */ /** Node's RAM (GB) */
ram: number; ram: number;
/** Node's used RAM */ /** Node's used RAM (GB) */
ramUsed: number; ramUsed: number;
/** Node's number of cores */ /** Node's number of cores */
cores: number; cores: number;
@ -956,6 +961,78 @@ export interface SleeveTask {
factionWorkType: string; factionWorkType: string;
} }
/**
* Object representing a port. A port is a serialized queue.
* @public
*/
export interface NetscriptPort {
/**
* Write data to a port.
* @remarks
* RAM cost: 0 GB
*
* @returns The data popped off the queue if it was full.
*/
write(value: string|number): null|string|number;
/**
* Attempt to write data to the port.
* @remarks
* RAM cost: 0 GB
*
* @returns True if the data was added to the port, false if the port was full
*/
tryWrite(value: string|number): boolean;
/**
* Shift an element out of the port.
* @remarks
* RAM cost: 0 GB
*
* This function will remove the first element from the port and return it.
* If the port is empty, then the string NULL PORT DATA will be returned.
* @returns the data read.
*/
read(): string|number;
/**
* Retrieve the first element from the port without removing it.
* @remarks
* RAM cost: 0 GB
*
* This function is used to peek at the data from a port. It returns the
* first element in the specified port without removing that element. If
* the port is empty, the string NULL PORT DATA will be returned.
* @returns the data read
*/
peek(): string|number;
/**
* Check if the port is full.
* @remarks
* RAM cost: 0 GB
*
* @returns true if the port is full, otherwise false
*/
full(): boolean;
/**
* Check if the port is empty.
* @remarks
* RAM cost: 0 GB
*
* @returns true if the port is empty, otherwise false
*/
empty(): boolean;
/**
* Empties all data from the port.
* @remarks
* RAM cost: 0 GB
*/
clear(): void;
}
/** /**
* Stock market API * Stock market API
* @public * @public
@ -1213,6 +1290,8 @@ export interface TIX {
* @remarks * @remarks
* RAM cost: 2.5 GB * RAM cost: 2.5 GB
* This is an object containing information for all the Limit and Stop Orders you have in the stock market. * This is an object containing information for all the Limit and Stop Orders you have in the stock market.
* For each symbol you have a position in, the returned object will have a key with that symbol's name.
* The object's properties are each an array of {@link StockOrderObject}
* The object has the following structure: * The object has the following structure:
* *
* ```ts * ```ts
@ -4440,11 +4519,11 @@ export interface NS extends Singularity {
* //Get logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"] * //Get logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
* ns.tail("foo.script", "foodnstuff", 1, "test"); * ns.tail("foo.script", "foodnstuff", 1, "test");
* ``` * ```
* @param fn - Optional. Filename of the script being tailed. If omitted, the current script is tailed. * @param fn - Optional. Filename or PID of the script being tailed. If omitted, the current script is tailed.
* @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional. * @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional.
* @param args - Arguments for the script being tailed. * @param args - Arguments for the script being tailed.
*/ */
tail(fn?: string, host?: string, ...args: any[]): void; tail(fn?: FilenameOrPID, host?: string, ...args: any[]): void;
/** /**
* Get the list of servers connected to a server. * Get the list of servers connected to a server.
@ -5094,7 +5173,7 @@ export interface NS extends Singularity {
* const [totalRam, ramUsed] = ns.getServerRam("helios"); * const [totalRam, ramUsed] = ns.getServerRam("helios");
* ``` * ```
* @param host - Host of target server. * @param host - Host of target server.
* @returns Array with total and used memory on the specified server. * @returns Array with total and used memory on the specified server, in GB.
*/ */
getServerRam(host: string): [number, number]; getServerRam(host: string): [number, number];
@ -5104,7 +5183,7 @@ export interface NS extends Singularity {
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* @param host - Hostname of the target server. * @param host - Hostname of the target server.
* @returns max ram * @returns max ram (GB)
*/ */
getServerMaxRam(host: string): number; getServerMaxRam(host: string): number;
/** /**
@ -5113,7 +5192,7 @@ export interface NS extends Singularity {
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* @param host - Hostname of the target server. * @param host - Hostname of the target server.
* @returns used ram * @returns used ram (GB)
*/ */
getServerUsedRam(host: string): number; getServerUsedRam(host: string): number;
@ -5187,6 +5266,7 @@ export interface NS extends Singularity {
* RAM cost: 0.1 GB * RAM cost: 0.1 GB
* *
* Returns a boolean indicating whether the specified script is running on the target server. * Returns a boolean indicating whether the specified script is running on the target server.
* If you use a PID instead of a filename, the hostname and args parameters are unnecessary.
* Remember that a script is uniquely identified by both its name and its arguments. * Remember that a script is uniquely identified by both its name and its arguments.
* *
* @example * @example
@ -5213,12 +5293,12 @@ export interface NS extends Singularity {
* //The function call will return true if there is a script named foo.script running with the arguments 1, 5, and “test” (in that order) on the joesguns server, and false otherwise: * //The function call will return true if there is a script named foo.script running with the arguments 1, 5, and “test” (in that order) on the joesguns server, and false otherwise:
* ns.isRunning("foo.script", "joesguns", 1, 5, "test"); * ns.isRunning("foo.script", "joesguns", 1, 5, "test");
* ``` * ```
* @param script - Filename of script to check. This is case-sensitive. * @param script - Filename or PID of script to check. This is case-sensitive.
* @param host - Host of target server. * @param host - Host of target server.
* @param args - Arguments to specify/identify which scripts to search for. * @param args - Arguments to specify/identify which scripts to search for.
* @returns True if specified script is running on the target server, and false otherwise. * @returns True if specified script is running on the target server, and false otherwise.
*/ */
isRunning(script: string, host: string, ...args: string[]): boolean; isRunning(script: FilenameOrPID, host: string, ...args: string[]): boolean;
/** /**
* Get general info about a running script. * Get general info about a running script.
@ -5226,10 +5306,14 @@ export interface NS extends Singularity {
* RAM cost: 0.3 GB * RAM cost: 0.3 GB
* *
* Running with no args returns curent script. * Running with no args returns curent script.
* If you use a PID as the first parameter, the hostname and args parameters are unnecessary.
* *
* @param filename - Optional. Filename or PID of the script.
* @param hostname - Optional. Name of host server the script is running on.
* @param args - Arguments to identify the script
* @returns info about a running script * @returns info about a running script
*/ */
getRunningScript(filename?: string | number, hostname?: string, ...args: (string | number)[]): RunningScript; getRunningScript(filename?: FilenameOrPID, hostname?: string, ...args: (string | number)[]): RunningScript;
/** /**
* Get cost of purchasing a server. * Get cost of purchasing a server.
@ -5252,7 +5336,7 @@ export interface NS extends Singularity {
* ns.tprint(i + " -- " + ns.getPurchasedServerCost(Math.pow(2, i))); * ns.tprint(i + " -- " + ns.getPurchasedServerCost(Math.pow(2, i)));
* } * }
* ``` * ```
* @param ram - Amount of RAM of a potential purchased server. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20). * @param ram - Amount of RAM of a potential purchased server, in GB. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
* @returns The cost to purchase a server with the specified amount of ram. * @returns The cost to purchase a server with the specified amount of ram.
*/ */
getPurchasedServerCost(ram: number): number; getPurchasedServerCost(ram: number): number;
@ -5300,7 +5384,7 @@ export interface NS extends Singularity {
* } * }
* ``` * ```
* @param hostname - Host of the purchased server. * @param hostname - Host of the purchased server.
* @param ram - Amount of RAM of the purchased server. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20). * @param ram - Amount of RAM of the purchased server, in GB. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
* @returns The hostname of the newly purchased server. * @returns The hostname of the newly purchased server.
*/ */
purchaseServer(hostname: string, ram: number): string; purchaseServer(hostname: string, ram: number): string;
@ -5341,7 +5425,7 @@ export interface NS extends Singularity {
* Returns the maximum RAM that a purchased server can have. * Returns the maximum RAM that a purchased server can have.
* *
* @remarks RAM cost: 0.05 GB * @remarks RAM cost: 0.05 GB
* @returns Returns the maximum RAM that a purchased server can have. * @returns Returns the maximum RAM (in GB) that a purchased server can have.
*/ */
getPurchasedServerMaxRam(): number; getPurchasedServerMaxRam(): number;
@ -5350,7 +5434,7 @@ export interface NS extends Singularity {
* @remarks * @remarks
* RAM cost: 0 GB * RAM cost: 0 GB
* *
* This function can be used to either write data to a text file (.txt). * This function can be used to write data to a text file (.txt).
* *
* This function will write data to that text file. If the specified text file does not exist, * This function will write data to that text file. If the specified text file does not exist,
* then it will be created. The third argument mode, defines how the data will be written to * then it will be created. The third argument mode, defines how the data will be written to
@ -5359,7 +5443,7 @@ export interface NS extends Singularity {
* then the data will be written in append mode which means that the data will be added at the * then the data will be written in append mode which means that the data will be added at the
* end of the text file. * end of the text file.
* *
* @param handle - Port or text file that will be written to. * @param handle - Filename of the text file that will be written to.
* @param data - Data to write. * @param data - Data to write.
* @param mode - Defines the write mode. Only valid when writing to text files. * @param mode - Defines the write mode. Only valid when writing to text files.
*/ */
@ -5385,13 +5469,13 @@ export interface NS extends Singularity {
* @remarks * @remarks
* RAM cost: 0 GB * RAM cost: 0 GB
* *
* This function is used to read data from a port or from a text file (.txt). * This function is used to read data from a text file (.txt).
* *
* This function will return the data in the specified text * This function will return the data in the specified text
* file. If the text file does not exist, an empty string will be returned. * file. If the text file does not exist, an empty string will be returned.
* *
* @param handle - Port or text file to read from. * @param handle - Filename to read from.
* @returns Data in the specified text file or port. * @returns Data in the specified text file.
*/ */
read(handle: string): any; read(handle: string): any;
@ -5463,9 +5547,8 @@ export interface NS extends Singularity {
* *
* @see https://bitburner.readthedocs.io/en/latest/netscript/netscriptmisc.html#netscript-ports * @see https://bitburner.readthedocs.io/en/latest/netscript/netscriptmisc.html#netscript-ports
* @param port - Port number. Must be an integer between 1 and 20. * @param port - Port number. Must be an integer between 1 and 20.
* @returns Data in the specified port.
*/ */
getPortHandle(port: number): IPort; getPortHandle(port: number): NetscriptPort;
/** /**
* Delete a file. * Delete a file.
@ -5548,7 +5631,7 @@ export interface NS extends Singularity {
* *
* @param script - Filename of script. This is case-sensitive. * @param script - Filename of script. This is case-sensitive.
* @param host - Host of target server the script is located on. This is optional, If it is not specified then the function will se the current server as the target server. * @param host - Host of target server the script is located on. This is optional, If it is not specified then the function will se the current server as the target server.
* @returns Amount of RAM required to run the specified script on the target server, and 0 if the script does not exist. * @returns Amount of RAM (in GB) required to run the specified script on the target server, and 0 if the script does not exist.
*/ */
getScriptRam(script: string, host?: string): number; getScriptRam(script: string, host?: string): number;

@ -1,9 +1,13 @@
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles";
import { toString } from "lodash";
import React from "react"; import React from "react";
import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath } from "../../Terminal/DirectoryHelpers"; import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../../Terminal/DirectoryHelpers";
import { IRouter } from "../../ui/Router";
import { ITerminal } from "../ITerminal";
export function ls( export function ls(
terminal: ITerminal, terminal: ITerminal,
@ -113,7 +117,55 @@ export function ls(
allMessages.sort(); allMessages.sort();
folders.sort(); folders.sort();
function postSegments(segments: string[], style?: any): void { interface ClickableScriptRowProps {
row: string;
prefix: string;
hostname: string;
}
function ClickableScriptRow({ row, prefix, hostname }: ClickableScriptRowProps): React.ReactElement {
const classes = makeStyles((theme: Theme) =>
createStyles({
scriptLinksWrap: {
display: "flex",
color: theme.palette.warning.main,
},
scriptLink: {
cursor: "pointer",
textDecorationLine: "underline",
paddingRight: "1.15em",
"&:last-child": { padding: 0 },
},
}),
)();
const rowSplit = row
.split(" ")
.map((x) => x.trim())
.filter((x) => !!x);
function onScriptLinkClick(filename: string): void {
if (player.getCurrentServer().hostname !== hostname) {
return terminal.error(`File is not on this server, connect to ${hostname} and try again`);
}
if (filename.startsWith("/")) filename = filename.slice(1);
const filepath = terminal.getFilepath(`${prefix}${filename}`);
const code = toString(terminal.getScript(player, filepath)?.code);
router.toScriptEditor({ [filepath]: code });
}
return (
<span className={classes.scriptLinksWrap}>
{rowSplit.map((rowItem) => (
<span key={rowItem} className={classes.scriptLink} onClick={() => onScriptLinkClick(rowItem)}>
{rowItem}
</span>
))}
</span>
);
}
function postSegments(segments: string[], style?: any, linked?: boolean): void {
const maxLength = Math.max(...segments.map((s) => s.length)) + 1; const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
const filesPerRow = Math.floor(80 / maxLength); const filesPerRow = Math.floor(80 / maxLength);
for (let i = 0; i < segments.length; i++) { for (let i = 0; i < segments.length; i++) {
@ -128,7 +180,11 @@ export function ls(
if (!style) { if (!style) {
terminal.print(row); terminal.print(row);
} else { } else {
terminal.printRaw(<span style={style}>{row}</span>); if (linked) {
terminal.printRaw(<ClickableScriptRow row={row} prefix={prefix} hostname={server.hostname} />);
} else {
terminal.printRaw(<span style={style}>{row}</span>);
}
} }
} }
} }
@ -139,9 +195,9 @@ export function ls(
{ segments: allTextFiles }, { segments: allTextFiles },
{ segments: allPrograms }, { segments: allPrograms },
{ segments: allContracts }, { segments: allContracts },
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" } }, { segments: allScripts, style: { color: "yellow", fontStyle: "bold" }, linked: true },
].filter((g) => g.segments.length > 0); ].filter((g) => g.segments.length > 0);
for (let i = 0; i < groups.length; i++) { for (let i = 0; i < groups.length; i++) {
postSegments(groups[i].segments, groups[i].style); postSegments(groups[i].segments, groups[i].style, groups[i].linked);
} }
} }

@ -279,7 +279,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"i to i+n.", "i to i+n.",
"\n\nAssuming you are initially positioned", "\n\nAssuming you are initially positioned",
"at the start of the array, determine whether you are", "at the start of the array, determine whether you are",
"able to reach the last index exactly.\n\n", "able to reach the last index.\n\n",
"Your answer should be submitted as 1 or 0, representing true and false respectively", "Your answer should be submitted as 1 or 0, representing true and false respectively",
].join(" "); ].join(" ");
}, },

@ -1,5 +1,7 @@
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */ /* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
import { validateObject } from "./Validator";
interface IReviverValue { interface IReviverValue {
ctor: string; ctor: string;
data: any; data: any;
@ -26,7 +28,11 @@ export function Reviver(key: string, value: IReviverValue | null): any {
const ctor = Reviver.constructors[value.ctor]; const ctor = Reviver.constructors[value.ctor];
if (typeof ctor === "function" && typeof ctor.fromJSON === "function") { if (typeof ctor === "function" && typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value); const obj = ctor.fromJSON(value);
if (ctor.validationData !== undefined) {
validateObject(obj, ctor.validationData);
}
return obj;
} }
} }
return value; return value;

78
src/utils/Validator.ts Normal file

@ -0,0 +1,78 @@
export type ObjectValidator<T> = {
[key in keyof T]?: ParameterValidator<T, keyof T>;
}
interface ParameterValidatorObject<Type, Key extends keyof Type> {
default?: any;
min?: number;
max?: number;
func?: (obj: Type, validator: ObjectValidator<Type>, key: Key) => void;
}
type ParameterValidatorFunction<Type, Key extends keyof Type> = (obj: Type, key: Key) => void;
type ParameterValidator<Type, Key extends keyof Type> = ParameterValidatorObject<Type, Key> | ParameterValidatorFunction<Type, Key>
export function validateObject<Type extends Record<string, unknown>, Key extends keyof Type>(obj: Type, validator: ObjectValidator<Type>): void {
for (const key of Object.keys(validator) as Key[]) {
const paramValidator = validator[key];
if (paramValidator !== undefined) {
if (typeof paramValidator === 'function') {
paramValidator(obj, key);
} else {
if (paramValidator.func !== undefined) {
paramValidator.func(obj, validator, key);
} else {
if ((typeof obj[key]) !== (typeof paramValidator.default)) {
obj[key] = paramValidator.default
}
if (typeof obj[key] === 'number' && paramValidator.min !== undefined) {
if (obj[key] < paramValidator.min) obj[key] = paramValidator.min as Type[Key];
}
if (typeof obj[key] === 'number' && paramValidator.max !== undefined) {
if (obj[key] > paramValidator.max) obj[key] = paramValidator.max as Type[Key];
}
}
}
}
}
}
export function minMax<Type, Key extends keyof Type>(def: number, min: number, max: number): (obj: Type, key: Key & keyof Type) => void {
return (obj, key) => {
if (typeof obj[key] !== 'number') {
obj[key] = def as unknown as Type[Key];
return;
}
if ((obj[key] as unknown as number) < min) {
obj[key] = min as unknown as Type[Key];
}
if ((obj[key] as unknown as number) > max) {
obj[key] = max as unknown as Type[Key];
}
};
}
export function oneOf<Type, Key extends keyof Type, Value>(def: Value, options: Value[]): (obj: Type, key: Key & keyof Type) => void {
return (obj, key) => {
if (typeof obj[key] !== typeof def) {
obj[key] = def as unknown as Type[Key];
return;
}
if (!options.includes(obj[key] as unknown as Value)) {
obj[key] = def as unknown as Type[Key];
}
};
}
export function subsetOf<Type, Key extends keyof Type, Value>(options: Value[]): (obj: Type, key: Key & keyof Type) => void {
return (obj, key) => {
if (typeof obj[key] !== 'object' || !Array.isArray(obj[key])) {
obj[key] = [] as unknown as Type[Key];
return;
}
const validValues: Value[] = [];
for (const value of obj[key] as unknown as Value[]) {
if (options.includes(value)) validValues.push(value);
}
obj[key] = validValues as unknown as Type[Key];
};
}