mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-20 00:04:22 +02:00
Merge pull request #3985 from Snarling/synchronize
NETSCRIPT: ns.scp and ns.write are now synchronous + fix exec race condition
This commit is contained in:
+93
-144
@@ -829,142 +829,106 @@ const base: InternalAPI<NS> = {
|
||||
},
|
||||
scp:
|
||||
(ctx: NetscriptContext) =>
|
||||
async (
|
||||
_scriptname: unknown,
|
||||
_destination: unknown,
|
||||
_source: unknown = ctx.workerScript.hostname,
|
||||
): Promise<boolean> => {
|
||||
(_files: unknown, _destination: unknown, _source: unknown = ctx.workerScript.hostname): boolean => {
|
||||
const destination = helpers.string(ctx, "destination", _destination);
|
||||
const source = helpers.string(ctx, "source", _source);
|
||||
if (Array.isArray(_scriptname)) {
|
||||
// Recursively call scp on all elements of array
|
||||
const scripts: string[] = _scriptname;
|
||||
if (scripts.length === 0) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy");
|
||||
}
|
||||
let res = true;
|
||||
await Promise.all(
|
||||
scripts.map(async function (script) {
|
||||
if (!(await NetscriptFunctions(ctx.workerScript).scp(script, destination, source))) {
|
||||
res = false;
|
||||
}
|
||||
}),
|
||||
);
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
|
||||
const scriptName = helpers.string(ctx, "scriptName", _scriptname);
|
||||
|
||||
// Invalid file type
|
||||
if (!isValidFilePath(scriptName)) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${scriptName}'`);
|
||||
}
|
||||
|
||||
// Invalid file name
|
||||
if (!scriptName.endsWith(".lit") && !isScriptFilename(scriptName) && !scriptName.endsWith("txt")) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files");
|
||||
}
|
||||
|
||||
const destServer = helpers.getServer(ctx, destination);
|
||||
const currServ = helpers.getServer(ctx, source);
|
||||
const sourceServ = helpers.getServer(ctx, source);
|
||||
const files = Array.isArray(_files) ? _files : [_files];
|
||||
|
||||
// Scp for lit files
|
||||
if (scriptName.endsWith(".lit")) {
|
||||
let found = false;
|
||||
for (let i = 0; i < currServ.messages.length; ++i) {
|
||||
if (currServ.messages[i] == scriptName) {
|
||||
found = true;
|
||||
break;
|
||||
//First loop through filenames to find all errors before moving anything.
|
||||
for (const file of files) {
|
||||
// Not a string
|
||||
if (typeof file !== "string")
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "files should be a string or an array of strings.");
|
||||
|
||||
// Invalid file name
|
||||
if (!isValidFilePath(file)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${file}'`);
|
||||
|
||||
// Invalid file type
|
||||
if (!file.endsWith(".lit") && !isScriptFilename(file) && !file.endsWith("txt")) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files.");
|
||||
}
|
||||
}
|
||||
|
||||
let noFailures = true;
|
||||
//ts detects files as any[] here even though we would have thrown in the above loop if it wasn't string[]
|
||||
for (const file of files as string[]) {
|
||||
// Scp for lit files
|
||||
if (file.endsWith(".lit")) {
|
||||
const sourceMessage = sourceServ.messages.find((message) => message === file);
|
||||
if (!sourceMessage) {
|
||||
helpers.log(ctx, () => `File '${file}' does not exist.`);
|
||||
noFailures = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
helpers.log(ctx, () => `File '${scriptName}' does not exist.`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
for (let i = 0; i < destServer.messages.length; ++i) {
|
||||
if (destServer.messages[i] === scriptName) {
|
||||
helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`);
|
||||
return Promise.resolve(true); // Already exists
|
||||
const destMessage = destServer.messages.find((message) => message === file);
|
||||
if (destMessage) {
|
||||
helpers.log(ctx, () => `File '${file}' was already on '${destServer?.hostname}'.`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
destServer.messages.push(scriptName);
|
||||
helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
// Scp for text files
|
||||
if (scriptName.endsWith(".txt")) {
|
||||
let txtFile;
|
||||
for (let i = 0; i < currServ.textFiles.length; ++i) {
|
||||
if (currServ.textFiles[i].fn === scriptName) {
|
||||
txtFile = currServ.textFiles[i];
|
||||
break;
|
||||
destServer.messages.push(file);
|
||||
helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scp for text files
|
||||
if (file.endsWith(".txt")) {
|
||||
const sourceTextFile = sourceServ.textFiles.find((textFile) => textFile.fn === file);
|
||||
if (!sourceTextFile) {
|
||||
helpers.log(ctx, () => `File '${file}' does not exist.`);
|
||||
noFailures = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (txtFile === undefined) {
|
||||
helpers.log(ctx, () => `File '${scriptName}' does not exist.`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
for (let i = 0; i < destServer.textFiles.length; ++i) {
|
||||
if (destServer.textFiles[i].fn === scriptName) {
|
||||
// Overwrite
|
||||
destServer.textFiles[i].text = txtFile.text;
|
||||
helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`);
|
||||
return Promise.resolve(true);
|
||||
const destTextFile = destServer.textFiles.find((textFile) => textFile.fn === file);
|
||||
if (destTextFile) {
|
||||
destTextFile.text = sourceTextFile.text;
|
||||
helpers.log(ctx, () => `File '${file}' overwritten on '${destServer?.hostname}'.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const newFile = new TextFile(sourceTextFile.fn, sourceTextFile.text);
|
||||
destServer.textFiles.push(newFile);
|
||||
helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`);
|
||||
continue;
|
||||
}
|
||||
const newFile = new TextFile(txtFile.fn, txtFile.text);
|
||||
destServer.textFiles.push(newFile);
|
||||
helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`);
|
||||
return Promise.resolve(true);
|
||||
|
||||
// Scp for script files
|
||||
const sourceScript = sourceServ.scripts.find((script) => script.filename === file);
|
||||
if (!sourceScript) {
|
||||
helpers.log(ctx, () => `File '${file}' does not exist.`);
|
||||
noFailures = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Overwrite script if it already exists
|
||||
const destScript = destServer.scripts.find((script) => script.filename === file);
|
||||
if (destScript) {
|
||||
if (destScript.code === sourceScript.code) {
|
||||
helpers.log(ctx, () => `Identical file '${file}' was already on '${destServer?.hostname}'`);
|
||||
continue;
|
||||
}
|
||||
destScript.code = sourceScript.code;
|
||||
destScript.ramUsage = destScript.ramUsage;
|
||||
destScript.markUpdated();
|
||||
helpers.log(ctx, () => `WARNING: File '${file}' overwritten on '${destServer?.hostname}'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new script if it does not already exist
|
||||
const newScript = new Script(Player, file);
|
||||
newScript.code = sourceScript.code;
|
||||
newScript.ramUsage = sourceScript.ramUsage;
|
||||
newScript.server = destServer.hostname;
|
||||
destServer.scripts.push(newScript);
|
||||
helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`);
|
||||
newScript.updateRamUsage(Player, destServer.scripts);
|
||||
}
|
||||
|
||||
// Scp for script files
|
||||
let sourceScript = null;
|
||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
||||
if (scriptName == currServ.scripts[i].filename) {
|
||||
sourceScript = currServ.scripts[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sourceScript == null) {
|
||||
helpers.log(ctx, () => `File '${scriptName}' does not exist.`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
// Overwrite script if it already exists
|
||||
for (let i = 0; i < destServer.scripts.length; ++i) {
|
||||
if (scriptName == destServer.scripts[i].filename) {
|
||||
helpers.log(ctx, () => `WARNING: File '${scriptName}' overwritten on '${destServer?.hostname}'`);
|
||||
const oldScript = destServer.scripts[i];
|
||||
// If it's the exact same file don't actually perform the
|
||||
// copy to avoid recompiling uselessly. Players tend to scp
|
||||
// liberally.
|
||||
if (oldScript.code === sourceScript.code) return Promise.resolve(true);
|
||||
oldScript.code = sourceScript.code;
|
||||
oldScript.ramUsage = sourceScript.ramUsage;
|
||||
oldScript.markUpdated();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new script if it does not already exist
|
||||
const newScript = new Script(Player, scriptName);
|
||||
newScript.code = sourceScript.code;
|
||||
newScript.ramUsage = sourceScript.ramUsage;
|
||||
newScript.server = destServer.hostname;
|
||||
destServer.scripts.push(newScript);
|
||||
helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`);
|
||||
return new Promise((resolve) => {
|
||||
if (destServer === null) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
newScript.updateRamUsage(Player, destServer.scripts).then(() => resolve(true));
|
||||
});
|
||||
return noFailures;
|
||||
},
|
||||
ls:
|
||||
(ctx: NetscriptContext) =>
|
||||
@@ -1501,34 +1465,19 @@ const base: InternalAPI<NS> = {
|
||||
},
|
||||
write:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_port: unknown, data: unknown = "", _mode: unknown = "a"): Promise<void> => {
|
||||
(_port: unknown, _data: unknown = "", _mode: unknown = "a"): void => {
|
||||
const port = helpers.string(ctx, "port", _port);
|
||||
const data = helpers.string(ctx, "data", _data);
|
||||
const mode = helpers.string(ctx, "mode", _mode);
|
||||
if (isString(port)) {
|
||||
// Write to script or text file
|
||||
let fn = port;
|
||||
if (!isValidFilePath(fn)) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`);
|
||||
}
|
||||
if (!isValidFilePath(fn)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`);
|
||||
|
||||
if (fn.lastIndexOf("/") === 0) {
|
||||
fn = removeLeadingSlash(fn);
|
||||
}
|
||||
if (fn.lastIndexOf("/") === 0) fn = removeLeadingSlash(fn);
|
||||
|
||||
// Coerce 'data' to be a string
|
||||
try {
|
||||
data = String(data);
|
||||
} catch (e: unknown) {
|
||||
throw helpers.makeRuntimeErrorMsg(
|
||||
ctx,
|
||||
`Invalid data (${String(e)}). Data being written must be convertible to a string`,
|
||||
);
|
||||
}
|
||||
const server = helpers.getServer(ctx, ctx.workerScript.hostname);
|
||||
|
||||
const server = ctx.workerScript.getServer();
|
||||
if (server == null) {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev.");
|
||||
}
|
||||
if (isScriptFilename(fn)) {
|
||||
// Write to script
|
||||
let script = ctx.workerScript.getScriptOnServer(fn, server);
|
||||
@@ -1545,7 +1494,7 @@ const base: InternalAPI<NS> = {
|
||||
const txtFile = getTextFile(fn, server);
|
||||
if (txtFile == null) {
|
||||
createTextFile(fn, String(data), server);
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
if (mode === "w") {
|
||||
txtFile.write(String(data));
|
||||
@@ -1553,7 +1502,7 @@ const base: InternalAPI<NS> = {
|
||||
txtFile.append(String(data));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
return;
|
||||
} else {
|
||||
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user