Fixed numerous reported bugs. Refactored some of the directory-related code. Added documentation for MasonDs changes to hack/grow/weaken

This commit is contained in:
danielyxie 2019-05-11 19:20:20 -07:00
parent 29e0ce5f96
commit b0918d7bd3
19 changed files with 172 additions and 88 deletions

@ -134,10 +134,15 @@ A Stop Order to buy will execute if the stock's price <= order's price
A Stop Order to sell will execute if the stock's price >= order's price. A Stop Order to sell will execute if the stock's price >= order's price.
.. note:: Limit and Stop orders do **not** take into account the fact that .. note:: Stop Orders do **not** take into account the fact that transactions can
transactions can influence a stock's price. If a stock's price influence a stock's price. Limit Orders, however, do take this into account.
changes mid-transaction, a limit/stop order will continue to execute
even if its conditions are no longer met. For example, assume you have a Limit Order set to purchase a stock at
$5. Then, the stock's price drops to $5 and your Limit Order executes.
However, the transaction causes the stock's price to increase before
the order finishes executing all of the shares. Your Limit Order will
stop executing, and will continue only when the stock's price drops back to
$5 or below.
Automating the Stock Market Automating the Stock Market
--------------------------- ---------------------------

@ -4,6 +4,11 @@ grow() Netscript Function
.. js:function:: grow(hostname/ip) .. js:function:: grow(hostname/ip)
:param string hostname/ip: IP or hostname of the target server to grow :param string hostname/ip: IP or hostname of the target server to grow
:param object options: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
:returns: The number by which the money on the server was multiplied for the growth :returns: The number by which the money on the server was multiplied for the growth
:RAM cost: 0.15 GB :RAM cost: 0.15 GB
@ -19,3 +24,4 @@ grow() Netscript Function
Example:: Example::
grow("foodnstuff"); grow("foodnstuff");
grow("foodnstuff", { threads: 5 }); // Only use 5 threads to grow

@ -4,6 +4,11 @@ hack() Netscript Function
.. js:function:: hack(hostname/ip) .. js:function:: hack(hostname/ip)
:param string hostname/ip: IP or hostname of the target server to hack :param string hostname/ip: IP or hostname of the target server to hack
:param object options: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
:returns: The amount of money stolen if the hack is successful, and zero otherwise :returns: The amount of money stolen if the hack is successful, and zero otherwise
:RAM cost: 0.1 GB :RAM cost: 0.1 GB
@ -20,3 +25,4 @@ hack() Netscript Function
hack("foodnstuff"); hack("foodnstuff");
hack("10.1.2.3"); hack("10.1.2.3");
hack("foodnstuff", { threads: 5 }); // Only use 5 threads to hack

@ -1,9 +1,14 @@
weaken() Netscript Function weaken() Netscript Function
=========================== ===========================
.. js:function:: weaken(hostname/ip) .. js:function:: weaken(hostname/ip, options={})
:param string hostname/ip: IP or hostname of the target server to weaken :param string hostname/ip: IP or hostname of the target server to weaken
:param object options: Optional parameters for configuring function behavior. Properties:
* threads (*number*) - Number of threads to use for this function.
Must be less than or equal to the number of threads the script is running with.
:returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied :returns: The amount by which the target server's security level was decreased. This is equivalent to 0.05 multiplied
by the number of script threads by the number of script threads
:RAM cost: 0.15 GB :RAM cost: 0.15 GB
@ -18,3 +23,4 @@ weaken() Netscript Function
Example:: Example::
weaken("foodnstuff"); weaken("foodnstuff");
weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken

@ -5,7 +5,7 @@ workForFaction() Netscript Function
:param string factionName: Name of faction to work for. CASE-SENSITIVE :param string factionName: Name of faction to work for. CASE-SENSITIVE
:param string workType: :param string workType:
Type of work to perform for the faction Type of work to perform for the faction:
* hacking/hacking contracts/hackingcontracts * hacking/hacking contracts/hackingcontracts
* field/fieldwork/field work * field/fieldwork/field work

@ -233,9 +233,12 @@ export let CONSTANTS: IMap<any> = {
* Re-sleeves can no longer have the NeuroFlux Governor augmentation * Re-sleeves can no longer have the NeuroFlux Governor augmentation
** This is just a temporary patch until the mechanic gets re-worked ** This is just a temporary patch until the mechanic gets re-worked
* hack(), grow(), and weaken() functions now take optional arguments for number of threads to use (by MasonD)
* Adjusted RAM costs of Netscript Singularity functions (mostly increased) * Adjusted RAM costs of Netscript Singularity functions (mostly increased)
* Netscript Singularity functions no longer cost extra RAM outside of BitNode-4 * Netscript Singularity functions no longer cost extra RAM outside of BitNode-4
* Corporation employees no longer have an "age" stat * Corporation employees no longer have an "age" stat
* Gang Wanted level gain rate capped at 100 (per employee)
* Bug Fix: Corporation employees stats should no longer become negative * Bug Fix: Corporation employees stats should no longer become negative
* Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios * Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios
* Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server * Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server
@ -245,5 +248,9 @@ export let CONSTANTS: IMap<any> = {
* Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes * Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes
* Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads * Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads
* Bug Fix: Faction UI should now automatically update reputation * Bug Fix: Faction UI should now automatically update reputation
* Bug Fix: Fixed purchase4SMarketData()
* Bug Fix: Netscript1.0 now works properly for multiple 'namespace' imports (import * as namespace from "script")
* Bug Fix: Terminal 'wget' command now correctly evaluates directory paths
* Bug Fix: wget(), write(), and scp() Netscript functions now fail if an invalid filepath is passed in
` `
} }

@ -682,21 +682,25 @@ GangMember.prototype.calculateRespectGain = function(gang) {
GangMember.prototype.calculateWantedLevelGain = function(gang) { GangMember.prototype.calculateWantedLevelGain = function(gang) {
const task = this.getTask(); const task = this.getTask();
if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) {return 0;} if (task == null || !(task instanceof GangMemberTask) || task.baseWanted === 0) { return 0; }
var statWeight = (task.hackWeight/100) * this.hack + let statWeight = (task.hackWeight / 100) * this.hack +
(task.strWeight/100) * this.str + (task.strWeight / 100) * this.str +
(task.defWeight/100) * this.def + (task.defWeight / 100) * this.def +
(task.dexWeight/100) * this.dex + (task.dexWeight / 100) * this.dex +
(task.agiWeight/100) * this.agi + (task.agiWeight / 100) * this.agi +
(task.chaWeight/100) * this.cha; (task.chaWeight / 100) * this.cha;
statWeight -= (3.5 * task.difficulty); statWeight -= (3.5 * task.difficulty);
if (statWeight <= 0) { return 0; } if (statWeight <= 0) { return 0; }
const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100; const territoryMult = Math.pow(AllGangs[gang.facName].territory * 100, task.territory.wanted) / 100;
if (isNaN(territoryMult) || territoryMult <= 0) { return 0; } if (isNaN(territoryMult) || territoryMult <= 0) { return 0; }
if (task.baseWanted < 0) { if (task.baseWanted < 0) {
return 0.5 * task.baseWanted * statWeight * territoryMult; return 0.4 * task.baseWanted * statWeight * territoryMult;
} else { } else {
return 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8)); const calc = 7 * task.baseWanted / (Math.pow(3 * statWeight * territoryMult, 0.8));
// Put an arbitrary cap on this to prevent wanted level from rising too fast if the
// denominator is very small. Might want to rethink formula later
return Math.max(100, calc);
} }
} }

@ -6,7 +6,6 @@
*/ */
import { IReturnStatus } from "../types"; import { IReturnStatus } from "../types";
//import { HacknetServer } from "../Hacknet/HacknetServer";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";

@ -110,6 +110,7 @@ import {
getStockMarket4SDataCost, getStockMarket4SDataCost,
getStockMarket4STixApiCost getStockMarket4STixApiCost
} from "./StockMarket/StockMarketCosts"; } from "./StockMarket/StockMarketCosts";
import { isValidFilePath } from "./Terminal/DirectoryHelpers";
import { TextFile, getTextFile, createTextFile } from "./TextFile"; import { TextFile, getTextFile, createTextFile } from "./TextFile";
import { import {
@ -1013,8 +1014,14 @@ function NetscriptFunctions(workerScript) {
}); });
return res; return res;
} }
if (!scriptname.endsWith(".lit") && !isScriptFilename(scriptname) &&
!scriptname.endsWith("txt")) { // Invalid file type
if (!isValidFilePath(scriptname)) {
throw makeRuntimeRejectMsg(workerScript, `Error: scp() failed due to invalid filename: ${scriptname}`);
}
// Invalid file name
if (!scriptname.endsWith(".lit") && !isScriptFilename(scriptname) && !scriptname.endsWith("txt")) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: scp() does not work with this file type. It only works for .script, .lit, and .txt files"); throw makeRuntimeRejectMsg(workerScript, "ERROR: scp() does not work with this file type. It only works for .script, .lit, and .txt files");
} }
@ -1724,6 +1731,7 @@ function NetscriptFunctions(workerScript) {
if (workerScript.shouldLog("purchase4SMarketData")) { if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Purchased 4S Market Data"); workerScript.log("Purchased 4S Market Data");
} }
displayStockMarketContent();
return true; return true;
}, },
purchase4SMarketDataTixApi : function() { purchase4SMarketDataTixApi : function() {
@ -1749,6 +1757,7 @@ function NetscriptFunctions(workerScript) {
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) { if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Purchased 4S Market Data TIX API"); workerScript.log("Purchased 4S Market Data TIX API");
} }
displayStockMarketContent();
return true; return true;
}, },
getPurchasedServerLimit : function() { getPurchasedServerLimit : function() {
@ -1919,8 +1928,12 @@ function NetscriptFunctions(workerScript) {
} }
return port.write(data); return port.write(data);
} else if (isString(port)) { // Write to script or text file } else if (isString(port)) { // Write to script or text file
var fn = port; const fn = port;
var server = workerScript.getServer(); if (!isValidFilePath(fn)) {
throw makeRuntimeRejectMsg(workerScript, `write() failed due to invalid filepath: ${fn}`);
}
const server = workerScript.getServer();
if (server == null) { if (server == null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in write(). This is a bug please contact game dev"); throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in write(). This is a bug please contact game dev");
} }
@ -2251,7 +2264,7 @@ function NetscriptFunctions(workerScript) {
}, },
wget: async function(url, target, ip=workerScript.serverIp) { wget: async function(url, target, ip=workerScript.serverIp) {
if (!isScriptFilename(target) && !target.endsWith(".txt")) { if (!isScriptFilename(target) && !target.endsWith(".txt")) {
workerSript.log(`ERROR: wget() failed because of an invalid target file: ${target}. Target file must be a script or text file`); workerScript.log(`ERROR: wget() failed because of an invalid target file: ${target}. Target file must be a script or text file`);
return Promise.resolve(false); return Promise.resolve(false);
} }
var s = safeGetServer(ip, "wget"); var s = safeGetServer(ip, "wget");

@ -178,7 +178,6 @@ function startNetscript1Script(workerScript) {
fnArgs.push(arguments[i]); fnArgs.push(arguments[i]);
} }
} }
console.log(fnArgs);
let cb = arguments[arguments.length-1]; let cb = arguments[arguments.length-1];
let fnPromise = entry.apply(null, fnArgs); let fnPromise = entry.apply(null, fnArgs);
fnPromise.then(function(res) { fnPromise.then(function(res) {
@ -289,7 +288,7 @@ function startNetscript1Script(workerScript) {
*/ */
function processNetscript1Imports(code, workerScript) { function processNetscript1Imports(code, workerScript) {
//allowReserved prevents 'import' from throwing error in ES5 //allowReserved prevents 'import' from throwing error in ES5
var ast = parse(code, {ecmaVersion:6, allowReserved:true, sourceType:"module"}); const ast = parse(code, { ecmaVersion: 6, allowReserved: true, sourceType: "module" });
var server = workerScript.getServer(); var server = workerScript.getServer();
if (server == null) { if (server == null) {
@ -305,10 +304,10 @@ function processNetscript1Imports(code, workerScript) {
return null; return null;
} }
var generatedCode = ""; //Generated Javascript Code let generatedCode = ""; // Generated Javascript Code
var hasImports = false; let hasImports = false;
//Walk over the tree and process ImportDeclaration nodes // Walk over the tree and process ImportDeclaration nodes
walk.simple(ast, { walk.simple(ast, {
ImportDeclaration: (node) => { ImportDeclaration: (node) => {
hasImports = true; hasImports = true;
@ -323,7 +322,7 @@ function processNetscript1Imports(code, workerScript) {
let scriptAst = parse(script.code, {ecmaVersion:5, allowReserved:true, sourceType:"module"}); let scriptAst = parse(script.code, {ecmaVersion:5, allowReserved:true, sourceType:"module"});
if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") { if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") {
//import * as namespace from script // import * as namespace from script
let namespace = node.specifiers[0].local.name; let namespace = node.specifiers[0].local.name;
let fnNames = []; //Names only let fnNames = []; //Names only
let fnDeclarations = []; //FunctionDeclaration Node objects let fnDeclarations = []; //FunctionDeclaration Node objects
@ -335,7 +334,7 @@ function processNetscript1Imports(code, workerScript) {
}); });
//Now we have to generate the code that would create the namespace //Now we have to generate the code that would create the namespace
generatedCode = generatedCode +=
"var " + namespace + ";\n" + "var " + namespace + ";\n" +
"(function (namespace) {\n"; "(function (namespace) {\n";
@ -406,6 +405,7 @@ function processNetscript1Imports(code, workerScript) {
//Add the imported code and re-generate in ES5 (JS Interpreter for NS1 only supports ES5); //Add the imported code and re-generate in ES5 (JS Interpreter for NS1 only supports ES5);
code = generatedCode + code; code = generatedCode + code;
var res = { var res = {
code: code, code: code,
lineOffset: lineOffset lineOffset: lineOffset

@ -0,0 +1,5 @@
export enum RamCalculationErrorCode {
SyntaxError = -1,
ImportError = -2,
URLImportError = -3,
}

@ -1,6 +1,8 @@
// Calculate a script's RAM usage // Calculate a script's RAM usage
import * as walk from "acorn-walk"; import * as walk from "acorn-walk";
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator"; import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
import { parse, Node } from "../../utils/acorn"; import { parse, Node } from "../../utils/acorn";
@ -71,12 +73,12 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
} }
} catch(e) { } catch(e) {
console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`); console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`);
return -1; return RamCalculationErrorCode.URLImportError;
} }
} else { } else {
if (!Array.isArray(otherScripts)) { if (!Array.isArray(otherScripts)) {
console.warn(`parseOnlyRamCalculate() not called with array of scripts`); console.warn(`parseOnlyRamCalculate() not called with array of scripts`);
return -1; return RamCalculationErrorCode.ImportError;
} }
let script = null; let script = null;
@ -89,8 +91,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
} }
if (script == null) { if (script == null) {
console.warn("Invalid script"); return RamCalculationErrorCode.ImportError; // No such script on the server
return -1; // No such script on the server.
} }
code = script.code; code = script.code;
@ -191,7 +192,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
// console.info("parse or eval error: ", error); // console.info("parse or eval error: ", error);
// This is not unexpected. The user may be editing a script, and it may be in // This is not unexpected. The user may be editing a script, and it may be in
// a transitory invalid state. // a transitory invalid state.
return -1; return RamCalculationErrorCode.SyntaxError;
} }
} }
@ -310,8 +311,8 @@ export async function calculateRamUsage(codeCopy, otherScripts) {
} catch (e) { } catch (e) {
console.error(`Failed to parse script for RAM calculations:`); console.error(`Failed to parse script for RAM calculations:`);
console.error(e); console.error(e);
return -1; return RamCalculationErrorCode.SyntaxError;
} }
return -1; return RamCalculationErrorCode.SyntaxError;
} }

@ -89,7 +89,7 @@ export class Script {
// Updates the script's RAM usage based on its code // Updates the script's RAM usage based on its code
async updateRamUsage(otherScripts: Script[]) { async updateRamUsage(otherScripts: Script[]) {
var res = await calculateRamUsage(this.code, otherScripts); var res = await calculateRamUsage(this.code, otherScripts);
if (res !== -1) { if (res > 0) {
this.ramUsage = roundToTwo(res); this.ramUsage = roundToTwo(res);
} }
} }

@ -1,5 +1,6 @@
import { Script } from "./Script"; import { Script } from "./Script";
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
import { calculateRamUsage } from "./RamCalculations"; import { calculateRamUsage } from "./RamCalculations";
import { isScriptFilename } from "./ScriptHelpersTS"; import { isScriptFilename } from "./ScriptHelpersTS";
@ -190,10 +191,22 @@ export async function updateScriptEditorContent() {
var codeCopy = code.repeat(1); var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts); var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts);
if (ramUsage !== -1) { if (ramUsage > 0) {
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB"; scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB";
} else { } else {
scriptEditorRamText.innerText = "RAM: Syntax Error"; switch (ramUsage) {
case RamCalculationErrorCode.ImportError:
scriptEditorRamText.innerText = "RAM: Import Error";
break;
case RamCalculationErrorCode.URLImportError:
scriptEditorRamText.innerText = "RAM: HTTP Import Error";
break;
case RamCalculationErrorCode.SyntaxError:
default:
scriptEditorRamText.innerText = "RAM: Syntax Error";
break;
}
} }
} }

@ -5,6 +5,7 @@ import { CodingContract } from "../CodingContracts";
import { Message } from "../Message/Message"; import { Message } from "../Message/Message";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { Script } from "../Script/Script"; import { Script } from "../Script/Script";
import { isValidFilePath } from "../Terminal/DirectoryHelpers";
import { TextFile } from "../TextFile"; import { TextFile } from "../TextFile";
import { IReturnStatus } from "../types"; import { IReturnStatus } from "../types";
@ -223,10 +224,10 @@ export class BaseServer {
* Overwrites existing files. Creates new files if the script does not eixst * Overwrites existing files. Creates new files if the script does not eixst
*/ */
writeToScriptFile(fn: string, code: string) { writeToScriptFile(fn: string, code: string) {
var ret = {success: false, overwritten: false}; var ret = { success: false, overwritten: false };
if (!isScriptFilename(fn)) { return ret; } if (!isValidFilePath(fn) || !isScriptFilename(fn)) { return ret; }
//Check if the script already exists, and overwrite it if it does // Check if the script already exists, and overwrite it if it does
for (let i = 0; i < this.scripts.length; ++i) { for (let i = 0; i < this.scripts.length; ++i) {
if (fn === this.scripts[i].filename) { if (fn === this.scripts[i].filename) {
let script = this.scripts[i]; let script = this.scripts[i];
@ -239,7 +240,7 @@ export class BaseServer {
} }
} }
//Otherwise, create a new script // Otherwise, create a new script
const newScript = new Script(fn, code, this.ip, this.scripts); const newScript = new Script(fn, code, this.ip, this.scripts);
this.scripts.push(newScript); this.scripts.push(newScript);
ret.success = true; ret.success = true;
@ -250,9 +251,9 @@ export class BaseServer {
// Overwrites existing files. Creates new files if the text file does not exist // Overwrites existing files. Creates new files if the text file does not exist
writeToTextFile(fn: string, txt: string) { writeToTextFile(fn: string, txt: string) {
var ret = { success: false, overwritten: false }; var ret = { success: false, overwritten: false };
if (!fn.endsWith("txt")) { return ret; } if (!isValidFilePath(fn) || !fn.endsWith("txt")) { return ret; }
//Check if the text file already exists, and overwrite if it does // Check if the text file already exists, and overwrite if it does
for (let i = 0; i < this.textFiles.length; ++i) { for (let i = 0; i < this.textFiles.length; ++i) {
if (this.textFiles[i].fn === fn) { if (this.textFiles[i].fn === fn) {
ret.overwritten = true; ret.overwritten = true;
@ -262,7 +263,7 @@ export class BaseServer {
} }
} }
//Otherwise create a new text file // Otherwise create a new text file
var newFile = new TextFile(fn, txt); var newFile = new TextFile(fn, txt);
this.textFiles.push(newFile); this.textFiles.push(newFile);
ret.success = true; ret.success = true;

@ -1475,7 +1475,7 @@ let Terminal = {
} }
let url = commandArray[1]; let url = commandArray[1];
let target = commandArray[2]; let target = Terminal.getFilepath(commandArray[2]);
if (!isScriptFilename(target) && !target.endsWith(".txt")) { if (!isScriptFilename(target) && !target.endsWith(".txt")) {
return post(`wget failed: Invalid target file. Target file must be script or text file`); return post(`wget failed: Invalid target file. Target file must be script or text file`);
} }

@ -1,9 +1,12 @@
/** /**
* Helper functions that implement "directory" functionality in the Terminal. * Helper functions that implement "directory" functionality in the Terminal.
* These aren't real directories, they're more of a pseudo-directory implementation * These aren't "real" directories, it's more of a pseudo-directory implementation
* that uses mainly string manipulation.
*
* This file contains functions that deal only with that string manipulation.
* Functions that need to access/process Server-related things can be
* found in ./DirectoryServerHelpers.ts
*/ */
import { HacknetServer } from "../Hacknet/HacknetServer";
import { Server } from "../Server/Server";
/** /**
* Removes leading forward slash ("/") from a string. * Removes leading forward slash ("/") from a string.
@ -149,44 +152,6 @@ export function getAllParentDirectories(path: string): string {
return t_path.slice(0, lastSlash + 1); return t_path.slice(0, lastSlash + 1);
} }
/**
* Given a directory (by the full directory path) and a server, returns all
* subdirectories of that directory. This is only for FIRST-LEVEl/immediate subdirectories
*/
export function getSubdirectories(serv: Server | HacknetServer, dir: string): string[] {
const res: string[] = [];
if (!isValidDirectoryPath(dir)) { return res; }
let t_dir = dir;
if (!t_dir.endsWith("/")) { t_dir += "/"; }
function processFile(fn: string) {
if (t_dir === "/" && isInRootDirectory(fn)) {
const subdir = getFirstParentDirectory(fn);
if (subdir !== "/" && !res.includes(subdir)) {
res.push(subdir);
}
} else if (fn.startsWith(t_dir)) {
const remaining = fn.slice(t_dir.length);
const subdir = getFirstParentDirectory(remaining);
if (subdir !== "/" && !res.includes(subdir)) {
res.push(subdir);
}
}
}
for (const script of serv.scripts) {
processFile(script.filename);
}
for (const txt of serv.textFiles) {
processFile(txt.fn);
}
return res;
}
/** /**
* Checks if a file path refers to a file in the root directory. * Checks if a file path refers to a file in the root directory.
*/ */

@ -0,0 +1,53 @@
/**
* Helper functions that implement "directory" functionality in the Terminal.
* These aren't "real" directories, it's more of a pseudo-directory implementation
* that uses mainly string manipulation.
*
* This file contains function that deal with Server-related directory things.
* Functions that deal with the string manipulation can be found in
* ./DirectoryHelpers.ts
*/
import {
isValidDirectoryPath,
isInRootDirectory,
getFirstParentDirectory,
} from "./DirectoryHelpers";
import { BaseServer } from "../Server/BaseServer";
/**
* Given a directory (by the full directory path) and a server, returns all
* subdirectories of that directory. This is only for FIRST-LEVEl/immediate subdirectories
*/
export function getSubdirectories(serv: BaseServer, dir: string): string[] {
const res: string[] = [];
if (!isValidDirectoryPath(dir)) { return res; }
let t_dir = dir;
if (!t_dir.endsWith("/")) { t_dir += "/"; }
function processFile(fn: string) {
if (t_dir === "/" && isInRootDirectory(fn)) {
const subdir = getFirstParentDirectory(fn);
if (subdir !== "/" && !res.includes(subdir)) {
res.push(subdir);
}
} else if (fn.startsWith(t_dir)) {
const remaining = fn.slice(t_dir.length);
const subdir = getFirstParentDirectory(remaining);
if (subdir !== "/" && !res.includes(subdir)) {
res.push(subdir);
}
}
}
for (const script of serv.scripts) {
processFile(script.filename);
}
for (const txt of serv.textFiles) {
processFile(txt.fn);
}
return res;
}

@ -1,8 +1,8 @@
import { import {
evaluateDirectoryPath, evaluateDirectoryPath,
getAllParentDirectories, getAllParentDirectories,
getSubdirectories,
} from "./DirectoryHelpers"; } from "./DirectoryHelpers";
import { getSubdirectories } from "./DirectoryServerHelpers";
import { import {
Aliases, Aliases,