bitburner-src/src/Electron.tsx

241 lines
7.7 KiB
TypeScript
Raw Normal View History

import { Player } from "@player";
import { Router } from "./ui/GameRoot";
2022-01-05 01:09:34 +01:00
import { Terminal } from "./Terminal";
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
2022-10-03 18:12:16 +02:00
import { IReturnStatus } from "./types";
import { GetServer } from "./Server/AllServers";
import { ImportPlayerData, SaveData, saveObject } from "./SaveObject";
import { exportScripts } from "./Terminal/commands/download";
import { CONSTANTS } from "./Constants";
import { hash } from "./hash/hash";
import { Buffer } from "buffer";
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
import { resolveFilePath } from "./Paths/FilePath";
import { hasScriptExtension } from "./Paths/ScriptFilePath";
2021-11-27 21:07:25 +01:00
2022-04-19 02:47:33 +02:00
interface IReturnWebStatus extends IReturnStatus {
data?: Record<string, unknown>;
}
declare global {
interface Window {
appNotifier: {
terminal: (message: string, type?: string) => void;
toast: (message: string, type: ToastVariant, duration?: number) => void;
};
appSaveFns: {
triggerSave: () => Promise<void>;
triggerGameExport: () => void;
triggerScriptsExport: () => void;
getSaveData: () => { save: string; fileName: string };
getSaveInfo: (base64save: string) => Promise<ImportPlayerData | undefined>;
pushSaveData: (base64save: string, automatic?: boolean) => void;
};
electronBridge: {
send: (channel: string, data?: unknown) => void;
2022-07-20 05:26:21 +02:00
receive: (channel: string, func: (...args: unknown[]) => void) => void;
2022-04-19 02:47:33 +02:00
};
}
interface Document {
getFiles: () => IReturnWebStatus;
deleteFile: (filename: string) => IReturnWebStatus;
saveFile: (filename: string, code: string) => IReturnWebStatus;
}
}
2021-11-27 21:07:25 +01:00
export function initElectron(): void {
const userAgent = navigator.userAgent.toLowerCase();
2022-01-05 01:09:34 +01:00
if (userAgent.indexOf(" electron/") > -1) {
// Electron-specific code
2022-04-19 02:47:33 +02:00
document.achievements = [];
initWebserver();
initAppNotifier();
initSaveFunctions();
initElectronBridge();
}
2021-11-27 21:07:25 +01:00
}
2021-12-21 19:41:11 +01:00
function initWebserver(): void {
2022-04-19 02:47:33 +02:00
document.getFiles = function (): IReturnWebStatus {
const home = GetServer("home");
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
if (home === null) return { res: false, msg: "Home server does not exist." };
return {
res: true,
data: {
files: [...home.scripts.values()].map((script) => ({
filename: script.filename,
code: script.code,
2022-01-26 06:58:02 +01:00
ramUsage: script.ramUsage,
})),
},
};
};
2022-04-19 02:47:33 +02:00
document.deleteFile = function (filename: string): IReturnWebStatus {
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
const path = resolveFilePath(filename);
if (!path) return { res: false, msg: "Invalid file path." };
2021-12-21 19:41:11 +01:00
const home = GetServer("home");
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
if (!home) return { res: false, msg: "Home server does not exist." };
return home.removeFile(path);
};
2022-04-19 02:47:33 +02:00
document.saveFile = function (filename: string, code: string): IReturnWebStatus {
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
const path = resolveFilePath(filename);
if (!path) return { res: false, msg: "Invalid file path." };
if (!hasScriptExtension(path)) return { res: false, msg: "Invalid file extension: must be a script" };
2021-12-21 19:41:11 +01:00
code = Buffer.from(code, "base64").toString();
const home = GetServer("home");
FILES: Path rework & typesafety (#479) * Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
2023-04-24 16:26:57 +02:00
if (!home) return { res: false, msg: "Home server does not exist." };
const { overwritten } = home.writeToScriptFile(path, code);
const script = home.scripts.get(path);
if (!script) return { res: false, msg: "Somehow failed to get script after writing it. This is a bug." };
const ramUsage = script.getRamUsage(home.scripts);
return { res: true, data: { overwritten, ramUsage } };
2021-12-21 19:41:11 +01:00
};
}
// Expose certain alert functions to allow the wrapper to sends message to the game
function initAppNotifier(): void {
const funcs = {
terminal: (message: string, type?: string) => {
2022-10-03 18:12:16 +02:00
const typesFn: Record<string, (s: string) => void> = {
info: Terminal.info,
warn: Terminal.warn,
error: Terminal.error,
2022-01-05 01:09:34 +01:00
success: Terminal.success,
};
let fn;
if (type) fn = typesFn[type];
if (!fn) fn = Terminal.print;
fn.bind(Terminal)(message);
},
toast: (message: string, type: ToastVariant, duration = 2000) => SnackbarEvents.emit(message, type, duration),
2022-01-05 01:09:34 +01:00
};
// Will be consumed by the electron wrapper.
2022-04-19 02:47:33 +02:00
window.appNotifier = funcs;
}
function initSaveFunctions(): void {
const funcs = {
triggerSave: (): Promise<void> => saveObject.saveGame(true),
triggerGameExport: (): void => {
try {
saveObject.exportGame();
} catch (error) {
2022-07-10 07:37:36 +02:00
console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
}
},
triggerScriptsExport: (): void => exportScripts("*", Player.getHomeComputer()),
getSaveData: (): { save: string; fileName: string } => {
return {
save: saveObject.getSaveString(),
fileName: saveObject.getSaveFileName(),
};
},
getSaveInfo: async (base64save: string): Promise<ImportPlayerData | undefined> => {
try {
const data = await saveObject.getImportDataFromString(base64save);
return data.playerData;
} catch (error) {
console.error(error);
return;
}
},
pushSaveData: (base64save: string, automatic = false): void => Router.toImportSave(base64save, automatic),
};
// Will be consumed by the electron wrapper.
2022-04-19 02:47:33 +02:00
window.appSaveFns = funcs;
}
function initElectronBridge(): void {
2022-04-19 02:47:33 +02:00
const bridge = window.electronBridge;
if (!bridge) return;
bridge.receive("get-save-data-request", () => {
2022-04-19 02:47:33 +02:00
const data = window.appSaveFns.getSaveData();
bridge.send("get-save-data-response", data);
});
2022-07-20 05:26:21 +02:00
bridge.receive("get-save-info-request", async (save: unknown) => {
if (typeof save !== "string") throw new Error("Error while trying to get save info");
2022-04-19 02:47:33 +02:00
const data = await window.appSaveFns.getSaveInfo(save);
bridge.send("get-save-info-response", data);
});
2022-07-20 05:26:21 +02:00
bridge.receive("push-save-request", (params: unknown) => {
if (typeof params !== "object") throw new Error("Error trying to push save request");
const { save, automatic = false } = params as { save: string; automatic: boolean };
2022-04-19 02:47:33 +02:00
window.appSaveFns.pushSaveData(save, automatic);
});
bridge.receive("trigger-save", () => {
2022-04-19 02:47:33 +02:00
return window.appSaveFns
.triggerSave()
.then(() => {
bridge.send("save-completed");
})
2022-04-19 02:47:33 +02:00
.catch((error: unknown) => {
2022-07-10 07:37:36 +02:00
console.error(error);
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
});
});
bridge.receive("trigger-game-export", () => {
try {
2022-04-19 02:47:33 +02:00
window.appSaveFns.triggerGameExport();
} catch (error) {
2022-07-10 07:37:36 +02:00
console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
}
});
bridge.receive("trigger-scripts-export", () => {
try {
2022-04-19 02:47:33 +02:00
window.appSaveFns.triggerScriptsExport();
} catch (error) {
2022-07-10 07:37:36 +02:00
console.error(error);
SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000);
}
});
}
export function pushGameSaved(data: SaveData): void {
2022-04-19 02:47:33 +02:00
const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-game-saved", data);
}
export function pushGameReady(): void {
2022-04-19 02:47:33 +02:00
const bridge = window.electronBridge;
if (!bridge) return;
// Send basic information to the electron wrapper
bridge.send("push-game-ready", {
player: {
identifier: Player.identifier,
playtime: Player.totalPlaytime,
lastSave: Player.lastSave,
},
game: {
version: CONSTANTS.VersionString,
hash: hash(),
},
});
}
export function pushImportResult(wasImported: boolean): void {
2022-04-19 02:47:33 +02:00
const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-import-result", { wasImported });
pushDisableRestore();
}
export function pushDisableRestore(): void {
2022-04-19 02:47:33 +02:00
const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-disable-restore", { duration: 1000 * 60 });
}