Implement GH Issue #620: Add tail() Netscript function

This commit is contained in:
danielyxie 2019-07-04 18:01:09 -07:00 committed by danielyxie
parent 8be7fa9157
commit 4cabd2e4ed
7 changed files with 107 additions and 27 deletions

@ -403,7 +403,7 @@ to convert from script to text file, or vice versa.
This function can also be used to rename files. This function can also be used to rename files.
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the .. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
full filepath. It cannot be a directory. full filepath. It cannot be a directory.
Examples:: Examples::
@ -511,6 +511,8 @@ sudov
Prints whether or not you have root access to the current server. Prints whether or not you have root access to the current server.
.. _tail_terminal_command:
tail tail
^^^^ ^^^^

@ -0,0 +1,29 @@
tail() Netscript Function
==================================
.. js:function:: tail([fn], [hostname/ip=current ip], [...args])
:param string fn: Optional. Filename of script to get logs from.
:param string ip: Optional. IP or hostname of the server that the script is on
:param args...: Arguments to identify which scripts to get logs for
:RAM cost: 0 GB
Opens a script's logs. This is functionally the same as the
:ref:`tail_terminal_command` Terminal command.
If the function is called with no arguments, it will open the current script's logs.
Otherwise, the `fn`, `hostname/ip,` and `args...` arguments can be used to get the logs
from another script. Remember that scripts are uniquely identified by both
their names and arguments.
Examples::
// Open logs from foo.script on the current server that was run with no args
tail("foo.script");
// Open logs from foo.script on the foodnstuff server that was run with no args
tail("foo.script", "foodnstuff");
// Open logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
tail("foo.script", "foodnstuff", 1, "test");

@ -24,6 +24,7 @@ This includes information such as function signatures, what they do, and their r
enableLog() <basicfunctions/enableLog> enableLog() <basicfunctions/enableLog>
isLogEnabled() <basicfunctions/isLogEnabled> isLogEnabled() <basicfunctions/isLogEnabled>
getScriptLogs() <basicfunctions/getScriptLogs> getScriptLogs() <basicfunctions/getScriptLogs>
tail() <basicfunctions/tail>
scan() <basicfunctions/scan> scan() <basicfunctions/scan>
nuke() <basicfunctions/nuke> nuke() <basicfunctions/nuke>
brutessh() <basicfunctions/brutessh> brutessh() <basicfunctions/brutessh>

@ -145,9 +145,10 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { is2DArray } from "./utils/helpers/is2DArray"; import { is2DArray } from "./utils/helpers/is2DArray";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
import { arrayToString } from "../utils/helpers/arrayToString";
import { formatNumber, isHTML } from "../utils/StringHelperFunctions"; import { formatNumber, isHTML } from "../utils/StringHelperFunctions";
import { logBoxCreate } from "../utils/LogBox";
import { arrayToString } from "../utils/helpers/arrayToString";
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { createElement } from "../utils/uiHelpers/createElement"; import { createElement } from "../utils/uiHelpers/createElement";
@ -251,7 +252,7 @@ function NetscriptFunctions(workerScript) {
/** /**
* Gets the Server for a specific hostname/ip, throwing an error * Gets the Server for a specific hostname/ip, throwing an error
* if the server doesn't exist. * if the server doesn't exist.
* @param {string} Hostname or IP of the server * @param {string} ip - Hostname or IP of the server
* @param {string} callingFnName - Name of calling function. For logging purposes * @param {string} callingFnName - Name of calling function. For logging purposes
* @returns {Server} The specified Server * @returns {Server} The specified Server
*/ */
@ -264,6 +265,59 @@ function NetscriptFunctions(workerScript) {
return server; return server;
} }
/**
* Searches for and returns the RunningScript object for the specified script.
* If the 'fn' argument is not specified, this returns the current RunningScript.
* @param {string} fn - Filename of script
* @param {string} ip - Hostname/ip of the server on which the script resides
* @param {any[]} scriptArgs - Running script's arguments
* @returns {RunningScript}
* Running script identified by the parameters, or null if no such script
* exists, or the current running script if the first argument 'fn'
* is not specified.
*/
const getRunningScript = function(fn, ip, callingFnName, scriptArgs) {
// Sanitize arguments
if (typeof callingFnName !== "string" || callingFnName === "") {
callingFnName = "getRunningScript";
}
if (!Array.isArray(scriptArgs)) {
throw makeRuntimeRejectMsg(
workerScript,
`Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` +
`This is probably a bug. Please report to game developer`
);
}
if (fn != null && typeof fn === "string") {
// Get Logs of another script
if (ip == null) { ip = workerScript.serverIp; }
const server = getServer(ip, callingFnName);
return findRunningScript(fn, scriptArgs, server);
}
// If no arguments are specified, return the current RunningScript
return workerScript.scriptRef;
}
/**
* Helper function for getting the error log message when the user specifies
* a nonexistent running script
* @param {string} fn - Filename of script
* @param {string} ip - Hostname/ip of the server on which the script resides
* @param {any[]} scriptArgs - Running script's arguments
* @returns {string} Error message to print to logs
*/
const getNoSuchRunningScriptErrorMessage = function(fn, ip, scriptArgs) {
if (!Array.isArray(scriptArgs)) {
scriptArgs = [];
}
return `Cannot find running script ${fn} on server ${ip} with args: ${arrayToString(scriptArgs)}`;
}
/** /**
* Checks if the player has TIX API access. Throws an error if the player does not * Checks if the player has TIX API access. Throws an error if the player does not
*/ */
@ -699,29 +753,23 @@ function NetscriptFunctions(workerScript) {
} }
return workerScript.disableLogs[fn] ? false : true; return workerScript.disableLogs[fn] ? false : true;
}, },
getScriptLogs: function(fn, ip) { getScriptLogs: function(fn, ip, ...scriptArgs) {
if (fn != null && typeof fn === 'string') { const runningScriptObj = getRunningScript(fn, ip, "getScriptLogs", scriptArgs);
// Get Logs of another script if (runningScriptObj == null) {
if (ip == null) { ip = workerScript.serverIp; } workerScript.log(`getScriptLogs() failed. ${getNoSuchRunningScriptErrorMessage(fn, ip, scriptArgs)}`);
const server = getServer(ip); return "";
if (server == null) {
workerScript.log(`getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
throw makeRuntimeRejectMsg(workerScript, `getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
}
let argsForTarget = [];
for (let i = 2; i < arguments.length; ++i) {
argsForTarget.push(arguments[i]);
}
const runningScriptObj = findRunningScript(fn, argsForTarget, server);
if (runningScriptObj == null) {
workerScript.scriptRef.log(`getScriptLogs() failed. No such script ${fn} on ${server.hostname} with args: ${arrayToString(argsForTarget)}`);
return "";
}
return runningScriptObj.logs.slice();
} }
return workerScript.scriptRef.logs.slice(); return runningScriptObj.logs.slice();
},
tail: function(fn, ip, ...scriptArgs) {
const runningScriptObj = getRunningScript(fn, ip, "tail", scriptArgs);
if (runningScriptObj == null) {
workerScript.log(`tail() failed. ${getNoSuchRunningScriptErrorMessage(fn, ip, scriptArgs)} `);
return;
}
logBoxCreate(runningScriptObj);
}, },
nuke: function(ip){ nuke: function(ip){
updateDynamicRam("nuke", getRamCost("nuke")); updateDynamicRam("nuke", getRamCost("nuke"));

@ -62,7 +62,7 @@ let NetscriptFunctions =
"hack|hackAnalyzeThreads|hackAnalyzePercent|hackChance|" + "hack|hackAnalyzeThreads|hackAnalyzePercent|hackChance|" +
"sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" + "sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" +
"ftpcrack|" + "ftpcrack|" +
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" + "clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|tail|" +
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" + "relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
"scp|ls|ps|hasRootAccess|" + "scp|ls|ps|hasRootAccess|" +
"getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" + "getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" +

@ -58,6 +58,7 @@ CodeMirror.defineMode("netscript", function(config, parserConfig) {
"enableLog": atom, "enableLog": atom,
"isLogEnabled": atom, "isLogEnabled": atom,
"getScriptLogs": atom, "getScriptLogs": atom,
"tail": atom,
"relaysmtp": atom, "relaysmtp": atom,
"httpworm": atom, "httpworm": atom,
"sqlinject": atom, "sqlinject": atom,

@ -25,7 +25,6 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const workerScript = props.workerScript; const workerScript = props.workerScript;
const scriptRef = workerScript.scriptRef; const scriptRef = workerScript.scriptRef;
const logClickHandler = logBoxCreate.bind(null, scriptRef); const logClickHandler = logBoxCreate.bind(null, scriptRef);
const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server); const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);