diff --git a/src/BitNode/BitNode.tsx b/src/BitNode/BitNode.tsx index 464afeabc..2d3012557 100644 --- a/src/BitNode/BitNode.tsx +++ b/src/BitNode/BitNode.tsx @@ -16,8 +16,11 @@ class BitNode { // BitNode number number: number; - constructor(n: number, name: string, desc = "", info: JSX.Element = <>) { + difficulty: 0 | 1 | 2; + + constructor(n: number, difficulty: 0 | 1 | 2, name: string, desc = "", info: JSX.Element = <>) { this.number = n; + this.difficulty = difficulty; this.name = name; this.desc = desc; this.info = info; @@ -28,6 +31,7 @@ export const BitNodes: IMap = {}; BitNodes["BitNode1"] = new BitNode( 1, + 0, "Source Genesis", "The original BitNode", ( @@ -54,6 +58,7 @@ BitNodes["BitNode1"] = new BitNode( ); BitNodes["BitNode2"] = new BitNode( 2, + 0, "Rise of the Underworld", "From the shadows, they rose", //Gangs ( @@ -101,6 +106,7 @@ BitNodes["BitNode2"] = new BitNode( ); BitNodes["BitNode3"] = new BitNode( 3, + 0, "Corporatocracy", "The Price of Civilization", ( @@ -140,6 +146,7 @@ BitNodes["BitNode3"] = new BitNode( ); BitNodes["BitNode4"] = new BitNode( 4, + 1, "The Singularity", "The Man and the Machine", ( @@ -164,6 +171,7 @@ BitNodes["BitNode4"] = new BitNode( ); BitNodes["BitNode5"] = new BitNode( 5, + 1, "Artificial Intelligence", "Posthuman", ( @@ -211,6 +219,7 @@ BitNodes["BitNode5"] = new BitNode( ); BitNodes["BitNode6"] = new BitNode( 6, + 1, "Bladeburners", "Like Tears in Rain", ( @@ -255,6 +264,7 @@ BitNodes["BitNode6"] = new BitNode( ); BitNodes["BitNode7"] = new BitNode( 7, + 2, "Bladeburners 2079", "More human than humans", ( @@ -303,6 +313,7 @@ BitNodes["BitNode7"] = new BitNode( ); BitNodes["BitNode8"] = new BitNode( 8, + 2, "Ghost of Wall Street", "Money never sleeps", ( @@ -347,6 +358,7 @@ BitNodes["BitNode8"] = new BitNode( ); BitNodes["BitNode9"] = new BitNode( 9, + 2, "Hacktocracy", "Hacknet Unleashed", ( @@ -389,6 +401,7 @@ BitNodes["BitNode9"] = new BitNode( ); BitNodes["BitNode10"] = new BitNode( 10, + 2, "Digital Carbon", "Your body is not who you are", ( @@ -428,6 +441,7 @@ BitNodes["BitNode10"] = new BitNode( ); BitNodes["BitNode11"] = new BitNode( 11, + 1, "The Big Crash", "Okay. Sell it all.", ( @@ -492,6 +506,7 @@ BitNodes["BitNode11"] = new BitNode( ); BitNodes["BitNode12"] = new BitNode( 12, + 0, "The Recursion", "Repeat.", ( @@ -507,18 +522,18 @@ BitNodes["BitNode12"] = new BitNode( ), ); // Books: Frontera, Shiner -BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes -BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON"); -BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON"); -BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON"); -BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON"); -BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON"); -BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON"); -BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON"); -BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON"); -BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON"); -BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON"); -BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON"); +BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes +BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON"); +BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON"); +BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON"); +BitNodes["BitNode17"] = new BitNode(17, 2, "", "COMING SOON"); +BitNodes["BitNode18"] = new BitNode(18, 2, "", "COMING SOON"); +BitNodes["BitNode19"] = new BitNode(19, 2, "", "COMING SOON"); +BitNodes["BitNode20"] = new BitNode(20, 2, "", "COMING SOON"); +BitNodes["BitNode21"] = new BitNode(21, 2, "", "COMING SOON"); +BitNodes["BitNode22"] = new BitNode(22, 2, "", "COMING SOON"); +BitNodes["BitNode23"] = new BitNode(23, 2, "", "COMING SOON"); +BitNodes["BitNode24"] = new BitNode(24, 2, "", "COMING SOON"); export function initBitNodeMultipliers(p: IPlayer): void { if (p.bitNodeN == null) { diff --git a/src/BitNode/ui/PortalPopup.tsx b/src/BitNode/ui/PortalPopup.tsx index dbcbfb3cf..dc1ec7ca0 100644 --- a/src/BitNode/ui/PortalPopup.tsx +++ b/src/BitNode/ui/PortalPopup.tsx @@ -29,6 +29,9 @@ export function PortalPopup(props: IProps): React.ReactElement { Source-File Level: {props.level} / {maxSourceFileLevel}

+ Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]} +
+
{bitNode.info}

diff --git a/src/DevMenu/ui/TimeSkip.tsx b/src/DevMenu/ui/TimeSkip.tsx index b08d8f531..ad25934dc 100644 --- a/src/DevMenu/ui/TimeSkip.tsx +++ b/src/DevMenu/ui/TimeSkip.tsx @@ -22,7 +22,7 @@ export function TimeSkip(props: IProps): React.ReactElement { return () => { props.player.lastUpdate -= time; props.engine._lastUpdate -= time; - saveObject.saveGame(props.engine.indexedDb); + saveObject.saveGame(); setTimeout(() => location.reload(), 1000); }; } diff --git a/src/SaveObject.d.ts b/src/SaveObject.d.ts index 38f2e0ab9..8d000a0fd 100644 --- a/src/SaveObject.d.ts +++ b/src/SaveObject.d.ts @@ -1,2 +1,6 @@ -export declare const saveObject: any; -export declare function openImportFileHandler(evt: any): void; +export declare const saveObject: { + getSaveString: () => string; + saveGame: () => void; + exportGame: () => void; +}; +export declare function loadGame(s: string): boolean; diff --git a/src/SaveObject.jsx b/src/SaveObject.jsx index 0476e1a73..6f2aaba3c 100755 --- a/src/SaveObject.jsx +++ b/src/SaveObject.jsx @@ -22,6 +22,7 @@ import * as ExportBonus from "./ExportBonus"; import { dialogBoxCreate } from "../utils/DialogBox"; import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners"; import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; +import { save } from "./db"; import Decimal from "decimal.js"; @@ -84,33 +85,12 @@ BitburnerSaveObject.prototype.getSaveString = function () { return saveString; }; -BitburnerSaveObject.prototype.saveGame = function (db) { - var saveString = this.getSaveString(); +BitburnerSaveObject.prototype.saveGame = function () { + const saveString = this.getSaveString(); - // We'll save to both localstorage and indexedDb - var objectStore = db.transaction(["savestring"], "readwrite").objectStore("savestring"); - var request = objectStore.put(saveString, "save"); - - request.onerror = function (e) { - console.error("Error saving game to IndexedDB: " + e); - }; - - try { - window.localStorage.setItem("bitburnerSave", saveString); - } catch (e) { - if (e.code == 22) { - createStatusText("Save failed for localStorage! Check console(F12)"); - console.error( - "Failed to save game to localStorage because the size of the save file " + - "is too large. However, the game will still be saved to IndexedDb if your browser " + - "supports it. If you would like to save to localStorage as well, then " + - "consider killing several of your scripts to " + - "fix this, or increasing the size of your browsers localStorage", - ); - } - } - - createStatusText("Game saved!"); + save(saveString) + .then(() => createStatusText("Game saved!")) + .catch((err) => console.error(err)); }; // Makes necessary changes to the loaded/imported data to ensure @@ -155,16 +135,9 @@ function evaluateVersionCompatibility(ver) { } function loadGame(saveString) { - if (saveString === "" || saveString == null || saveString === undefined) { - if (!window.localStorage.getItem("bitburnerSave")) { - return false; - } - saveString = decodeURIComponent(escape(atob(window.localStorage.getItem("bitburnerSave")))); - } else { - saveString = decodeURIComponent(escape(atob(saveString))); - } + saveString = decodeURIComponent(escape(atob(saveString))); - var saveObj = JSON.parse(saveString, Reviver); + const saveObj = JSON.parse(saveString, Reviver); loadPlayer(saveObj.PlayerSave); loadAllServers(saveObj.AllServersSave); @@ -224,13 +197,6 @@ function loadGame(saveString) { } else { Settings.init(); } - // if (saveObj.hasOwnProperty("FconfSettingsSave")) { - // try { - // loadFconf(saveObj.FconfSettingsSave); - // } catch (e) { - // console.error("ERROR: Failed to parse .fconf Settings."); - // } - // } if (saveObj.hasOwnProperty("LastExportBonus")) { try { ExportBonus.setLastExportBonus(JSON.parse(saveObj.LastExportBonus)); @@ -267,173 +233,6 @@ function loadGame(saveString) { return true; } -function loadImportedGame(saveObj, saveString) { - var tempSaveObj = null; - var tempPlayer = null; - - // Check to see if the imported save file can be parsed. If any - // errors are caught it will fail - try { - var decodedSaveString = decodeURIComponent(escape(atob(saveString))); - tempSaveObj = JSON.parse(decodedSaveString, Reviver); - - tempPlayer = JSON.parse(tempSaveObj.PlayerSave, Reviver); - - // Parse Decimal.js objects - tempPlayer.money = new Decimal(tempPlayer.money); - - JSON.parse(tempSaveObj.AllServersSave, Reviver); - JSON.parse(tempSaveObj.CompaniesSave, Reviver); - JSON.parse(tempSaveObj.FactionsSave, Reviver); - JSON.parse(tempSaveObj.SpecialServerIpsSave, Reviver); - if (tempSaveObj.hasOwnProperty("AliasesSave")) { - try { - JSON.parse(tempSaveObj.AliasesSave, Reviver); - } catch (e) { - console.error(`Parsing Aliases save failed: ${e}`); - } - } - if (tempSaveObj.hasOwnProperty("GlobalAliases")) { - try { - JSON.parse(tempSaveObj.AliasesSave, Reviver); - } catch (e) { - console.error(`Parsing Global Aliases save failed: ${e}`); - } - } - if (tempSaveObj.hasOwnProperty("MessagesSave")) { - try { - JSON.parse(tempSaveObj.MessagesSave, Reviver); - } catch (e) { - console.error(`Parsing Messages save failed: ${e}`); - initMessages(); - } - } else { - initMessages(); - } - if (saveObj.hasOwnProperty("StockMarketSave")) { - try { - JSON.parse(tempSaveObj.StockMarketSave, Reviver); - } catch (e) { - console.error(`Parsing StockMarket save failed: ${e}`); - } - } - if (saveObj.hasOwnProperty("LastExportBonus")) { - try { - if (saveObj.LastExportBonus) ExportBonus.setLastExportBonus(JSON.parse(saveObj.LastExportBonus)); - } catch (err) { - ExportBonus.setLastExportBonus(new Date().getTime()); - console.error("ERROR: Failed to parse last export bonus Settings " + err); - } - } - if (tempSaveObj.hasOwnProperty("VersionSave")) { - try { - var ver = JSON.parse(tempSaveObj.VersionSave, Reviver); - evaluateVersionCompatibility(ver); - } catch (e) { - console.error("Parsing Version save failed: " + e); - } - } - if (tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) { - try { - loadAllGangs(tempSaveObj.AllGangsSave); - } catch (e) { - console.error(`Failed to parse AllGangsSave: {e}`); - throw e; - } - } - } catch (e) { - dialogBoxCreate("Error importing game: " + e.toString()); - return false; - } - - // Since the save file is valid, load everything for real - saveString = decodeURIComponent(escape(atob(saveString))); - saveObj = JSON.parse(saveString, Reviver); - - loadPlayer(saveObj.PlayerSave); - loadAllServers(saveObj.AllServersSave); - loadCompanies(saveObj.CompaniesSave); - loadFactions(saveObj.FactionsSave); - loadSpecialServerIps(saveObj.SpecialServerIpsSave); - - if (saveObj.hasOwnProperty("AliasesSave")) { - try { - loadAliases(saveObj.AliasesSave); - } catch (e) { - loadAliases(""); - } - } else { - loadAliases(""); - } - if (saveObj.hasOwnProperty("GlobalAliasesSave")) { - try { - loadGlobalAliases(saveObj.GlobalAliasesSave); - } catch (e) { - loadGlobalAliases(""); - } - } else { - loadGlobalAliases(""); - } - if (saveObj.hasOwnProperty("MessagesSave")) { - try { - loadMessages(saveObj.MessagesSave); - } catch (e) { - initMessages(); - } - } else { - initMessages(); - } - if (saveObj.hasOwnProperty("StockMarketSave")) { - try { - loadStockMarket(saveObj.StockMarketSave); - } catch (e) { - loadStockMarket(""); - } - } else { - loadStockMarket(""); - } - if (saveObj.hasOwnProperty("SettingsSave")) { - try { - Settings.load(saveObj.SettingsSave); - } catch (e) { - Settings.init(); - } - } else { - Settings.init(); - } - // if (saveObj.hasOwnProperty("FconfSettingsSave")) { - // try { - // loadFconf(saveObj.FconfSettingsSave); - // } catch (e) { - // console.error("ERROR: Failed to load .fconf settings when importing"); - // } - // } - if (saveObj.hasOwnProperty("VersionSave")) { - try { - var ver = JSON.parse(saveObj.VersionSave, Reviver); - evaluateVersionCompatibility(ver); - - if (ver != CONSTANTS.Version) { - createNewUpdateText(); - } - } catch (e) { - createNewUpdateText(); - } - } else { - createNewUpdateText(); - } - if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) { - try { - loadAllGangs(saveObj.AllGangsSave); - } catch (e) { - console.error("ERROR: Failed to parse AllGangsSave: " + e); - } - } - saveObject.saveGame(Engine.indexedDb); - setTimeout(() => location.reload(), 1000); - return true; -} - BitburnerSaveObject.prototype.exportGame = function () { const saveString = this.getSaveString(); @@ -460,31 +259,6 @@ BitburnerSaveObject.prototype.exportGame = function () { } }; -BitburnerSaveObject.prototype.importGame = function () { - if (window.File && window.FileReader && window.FileList && window.Blob) { - var fileSelector = clearEventListeners("import-game-file-selector"); - fileSelector.addEventListener("change", openImportFileHandler, false); - $("#import-game-file-selector").click(); - } else { - dialogBoxCreate("ERR: Your browser does not support HTML5 File API. Cannot import."); - } -}; - -BitburnerSaveObject.prototype.deleteGame = function (db) { - // Delete from local storage - if (window.localStorage.getItem("bitburnerSave")) { - window.localStorage.removeItem("bitburnerSave"); - } - - // Delete from indexedDB - var request = db.transaction(["savestring"], "readwrite").objectStore("savestring").delete("save"); - request.onsuccess = function () {}; - request.onerror = function (e) { - console.error(`Failed to delete save from indexedDb: ${e}`); - }; - createStatusText("Game deleted!"); -}; - function createNewUpdateText() { dialogBoxCreate( "New update!
" + @@ -514,19 +288,4 @@ BitburnerSaveObject.fromJSON = function (value) { Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject; -function openImportFileHandler(evt) { - var file = evt.target.files[0]; - if (!file) { - dialogBoxCreate("Invalid file selected"); - return; - } - - var reader = new FileReader(); - reader.onload = function (e) { - var contents = e.target.result; - loadImportedGame(saveObject, contents); - }; - reader.readAsText(file); -} - -export { saveObject, loadGame, openImportFileHandler }; +export { saveObject, loadGame }; diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 6bdada4e1..db99967f2 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -61,7 +61,6 @@ import { scananalyze } from "./commands/scananalyze"; import { scp } from "./commands/scp"; import { sudov } from "./commands/sudov"; import { tail } from "./commands/tail"; -import { theme } from "./commands/theme"; import { top } from "./commands/top"; import { unalias } from "./commands/unalias"; import { wget } from "./commands/wget"; @@ -683,7 +682,6 @@ export class Terminal implements ITerminal { scp: scp, sudov: sudov, tail: tail, - theme: theme, top: top, unalias: unalias, wget: wget, diff --git a/src/Terminal/commands/theme.ts b/src/Terminal/commands/theme.ts deleted file mode 100644 index 62e468132..000000000 --- a/src/Terminal/commands/theme.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ITerminal } from "../ITerminal"; -import { IRouter } from "../../ui/Router"; -import { IPlayer } from "../../PersonObjects/IPlayer"; -import { BaseServer } from "../../Server/BaseServer"; -import { FconfSettings } from "../../Fconf/FconfSettings"; - -export function theme( - terminal: ITerminal, - router: IRouter, - player: IPlayer, - server: BaseServer, - args: (string | number)[], -): void { - if (args.length !== 1 && args.length !== 3) { - terminal.error("Incorrect number of arguments."); - terminal.error( - "Usage: theme [default|muted|solarized] | #[background color hex] #[text color hex] #[highlight color hex]", - ); - } else if (args.length === 1) { - const themeName = args[0]; - if (themeName == "default") { - document.body.style.setProperty("--my-highlight-color", "#ffffff"); - document.body.style.setProperty("--my-font-color", "#66ff33"); - document.body.style.setProperty("--my-background-color", "#000000"); - document.body.style.setProperty("--my-prompt-color", "#f92672"); - } else if (themeName == "muted") { - document.body.style.setProperty("--my-highlight-color", "#ffffff"); - document.body.style.setProperty("--my-font-color", "#66ff33"); - document.body.style.setProperty("--my-background-color", "#252527"); - } else if (themeName == "solarized") { - document.body.style.setProperty("--my-highlight-color", "#6c71c4"); - document.body.style.setProperty("--my-font-color", "#839496"); - document.body.style.setProperty("--my-background-color", "#002b36"); - } else { - return terminal.error("Theme not found"); - } - FconfSettings.THEME_HIGHLIGHT_COLOR = document.body.style.getPropertyValue("--my-highlight-color"); - FconfSettings.THEME_FONT_COLOR = document.body.style.getPropertyValue("--my-font-color"); - FconfSettings.THEME_BACKGROUND_COLOR = document.body.style.getPropertyValue("--my-background-color"); - FconfSettings.THEME_PROMPT_COLOR = document.body.style.getPropertyValue("--my-prompt-color"); - } else { - const inputBackgroundHex = args[0] + ""; - const inputTextHex = args[1] + ""; - const inputHighlightHex = args[2] + ""; - if ( - /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputBackgroundHex) && - /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputTextHex) && - /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputHighlightHex) - ) { - document.body.style.setProperty("--my-highlight-color", inputHighlightHex); - document.body.style.setProperty("--my-font-color", inputTextHex); - document.body.style.setProperty("--my-background-color", inputBackgroundHex); - FconfSettings.THEME_HIGHLIGHT_COLOR = document.body.style.getPropertyValue("--my-highlight-color"); - FconfSettings.THEME_FONT_COLOR = document.body.style.getPropertyValue("--my-font-color"); - FconfSettings.THEME_BACKGROUND_COLOR = document.body.style.getPropertyValue("--my-background-color"); - } else { - return terminal.error("Invalid Hex Input for theme"); - } - } -} diff --git a/src/db.tsx b/src/db.tsx new file mode 100644 index 000000000..ccfa3952a --- /dev/null +++ b/src/db.tsx @@ -0,0 +1,86 @@ +import { Engine } from "./engine"; +import { createStatusText } from "./ui/createStatusText"; + +function getDB(): Promise { + return new Promise((resolve, reject) => { + if (!window.indexedDB) { + reject("Indexed DB does not exists"); + } + /** + * DB is called bitburnerSave + * Object store is called savestring + * key for the Object store is called save + * Version `1` is important + */ + const indexedDbRequest: IDBOpenDBRequest = window.indexedDB.open("bitburnerSave", 1); + + // This is called when there's no db to begin with. It's important, don't remove it. + indexedDbRequest.onupgradeneeded = function (this: IDBRequest) { + const db = this.result; + db.createObjectStore("savestring"); + }; + + indexedDbRequest.onerror = function (this: IDBRequest, ev: Event) { + reject(`Failed to get IDB ${ev}`); + }; + + indexedDbRequest.onsuccess = function (this: IDBRequest, ev: Event) { + const db = this.result; + if (!db) { + reject("database loadign result was undefined"); + return; + } + resolve(db.transaction(["savestring"], "readwrite").objectStore("savestring")); + }; + }); +} + +interface ILoadCallback { + success: (s: string) => void; + error?: () => void; +} + +export function load(): Promise { + return new Promise(async (resolve, reject) => { + await getDB() + .then((db) => { + return new Promise((resolve, reject) => { + const request: IDBRequest = db.get("save"); + request.onerror = function (this: IDBRequest, ev: Event) { + reject("Error in Database request to get savestring: " + ev); + }; + + request.onsuccess = function (this: IDBRequest) { + resolve(this.result); + }; + }).then((saveString) => resolve(saveString)); + }) + .catch((r) => reject(r)); + }); +} + +interface ISaveCallback { + success: () => void; + error?: () => void; +} + +export function save(saveString: string): Promise { + return getDB().then((db) => { + return new Promise((resolve, reject) => { + // We'll save to both localstorage and indexedDb + const request = db.put(saveString, "save"); + + request.onerror = function (e) { + reject("Error saving game to IndexedDB: " + e); + }; + + request.onsuccess = () => resolve(); + }); + }); +} + +export function deleteGame(): Promise { + return getDB().then((db) => { + db.delete("save"); + }); +} diff --git a/src/engine.d.ts b/src/engine.d.ts index 46ed10fcd..4faff3450 100644 --- a/src/engine.d.ts +++ b/src/engine.d.ts @@ -1,3 +1 @@ -export declare function load(cb: () => void): void; - export declare const Engine: IEngine; diff --git a/src/engine.jsx b/src/engine.jsx index 544b8fc9f..7ed25ea6f 100644 --- a/src/engine.jsx +++ b/src/engine.jsx @@ -50,8 +50,6 @@ import "./Exploits/unclickable"; import React from "react"; const Engine = { - indexedDb: undefined, - // Time variables (milliseconds unix epoch time) _lastUpdate: new Date().getTime(), @@ -196,7 +194,7 @@ const Engine = { Engine.Counters.autoSaveCounter = Infinity; } else { Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5; - saveObject.saveGame(Engine.indexedDb); + saveObject.saveGame(); } } @@ -420,48 +418,4 @@ const Engine = { }, }; -function load(cb) { - if (!window.indexedDB) { - return Engine.load(null); // Will try to load from localstorage - } - - /** - * DB is called bitburnerSave - * Object store is called savestring - * key for the Object store is called save - */ - indexedDbRequest = window.indexedDB.open("bitburnerSave", 1); - - indexedDbRequest.onerror = function (e) { - console.error("Error opening indexedDB: "); - console.error(e); - Engine.load(null); // Try to load from localstorage - cb(); - }; - - indexedDbRequest.onsuccess = function (e) { - Engine.indexedDb = e.target.result; - var transaction = Engine.indexedDb.transaction(["savestring"]); - var objectStore = transaction.objectStore("savestring"); - var request = objectStore.get("save"); - request.onerror = function (e) { - console.error("Error in Database request to get savestring: " + e); - Engine.load(null); // Try to load from localstorage - cb(); - }; - - request.onsuccess = function () { - Engine.load(request.result); - cb(); - }; - }; - - indexedDbRequest.onupgradeneeded = function (e) { - const db = e.target.result; - db.createObjectStore("savestring"); - }; -} - -var indexedDbRequest; - -export { Engine, load }; +export { Engine }; diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 177ea480e..b1dcdd458 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -4,7 +4,7 @@ import { IPlayer } from "../PersonObjects/IPlayer"; import { IEngine } from "../IEngine"; import { ITerminal } from "../Terminal/ITerminal"; import { installAugmentations } from "../Augmentation/AugmentationHelpers"; -import { saveObject, openImportFileHandler } from "../SaveObject"; +import { saveObject } from "../SaveObject"; import { onExport } from "../ExportBonus"; import { LocationName } from "../Locations/data/LocationNames"; import { Location } from "../Locations/Location"; @@ -282,7 +282,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme {!ITutorial.isRunning ? ( - saveObject.saveGame(engine.indexedDb)} /> + saveObject.saveGame()} /> ) : ( )} @@ -357,10 +357,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme ) : page === Page.Options ? ( saveObject.saveGame(engine.indexedDb)} - delete={() => saveObject.deleteGame(engine.indexedDb)} + save={() => saveObject.saveGame()} export={() => saveObject.exportGame()} - import={openImportFileHandler} forceKill={() => { for (const hostname of Object.keys(AllServers)) { AllServers[hostname].runningScripts = []; diff --git a/src/ui/LoadingScreen.tsx b/src/ui/LoadingScreen.tsx index 27b63ab9f..92e10a6f1 100644 --- a/src/ui/LoadingScreen.tsx +++ b/src/ui/LoadingScreen.tsx @@ -4,56 +4,13 @@ import Typography from "@mui/material/Typography"; import Grid from "@mui/material/Grid"; import { Terminal } from "../Terminal"; -import { Engine } from "../engine"; +import { load } from "../db"; import { Player } from "../Player"; +import { Engine } from "../engine"; import { GameRoot } from "./GameRoot"; import { CONSTANTS } from "../Constants"; -function load(cb: () => void): void { - if (!window.indexedDB) { - return Engine.load(""); // Will try to load from localstorage - } - - /** - * DB is called bitburnerSave - * Object store is called savestring - * key for the Object store is called save - */ - // Version 1 is important - const indexedDbRequest: IDBOpenDBRequest = window.indexedDB.open("bitburnerSave", 1); - - indexedDbRequest.onerror = function (this: IDBRequest, ev: Event) { - console.error("Error opening indexedDB: "); - console.error(ev); - Engine.load(""); // Try to load from localstorage - cb(); - }; - - indexedDbRequest.onsuccess = function (this: IDBRequest) { - Engine.indexedDb = this.result; - const transaction = Engine.indexedDb.transaction(["savestring"]); - const objectStore = transaction.objectStore("savestring"); - const request: IDBRequest = objectStore.get("save"); - request.onerror = function (this: IDBRequest, ev: Event) { - console.error("Error in Database request to get savestring: " + ev); - Engine.load(""); // Try to load from localstorage - cb(); - }; - - request.onsuccess = function (this: IDBRequest) { - Engine.load(this.result); - cb(); - }; - }; - - // This is called when there's no db to begin with. It's important. - indexedDbRequest.onupgradeneeded = function (this: IDBRequest) { - const db = this.result; - db.createObjectStore("savestring"); - }; -} - export function LoadingScreen(): React.ReactElement { const [show, setShow] = useState(false); const [loaded, setLoaded] = useState(false); @@ -66,9 +23,19 @@ export function LoadingScreen(): React.ReactElement { }); useEffect(() => { - load(() => { - setLoaded(true); - }); + async function doLoad() { + await load() + .then((saveString) => { + Engine.load(saveString); + setLoaded(true); + }) + .catch((reason) => { + console.error(reason); + Engine.load(""); + setLoaded(true); + }); + } + doLoad(); }, []); if (loaded) { diff --git a/src/ui/React/GameOptionsRoot.tsx b/src/ui/React/GameOptionsRoot.tsx index 059bb9dca..375bfee4c 100644 --- a/src/ui/React/GameOptionsRoot.tsx +++ b/src/ui/React/GameOptionsRoot.tsx @@ -28,6 +28,7 @@ import { dialogBoxCreate } from "../../../utils/DialogBox"; import { ConfirmationModal } from "./ConfirmationModal"; import { Settings } from "../../Settings/Settings"; +import { save, deleteGame } from "../../db"; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -42,9 +43,7 @@ const useStyles = makeStyles((theme: Theme) => interface IProps { player: IPlayer; save: () => void; - delete: () => void; export: () => void; - import: (evt: any) => void; forceKill: () => void; softReset: () => void; } @@ -153,20 +152,38 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { Settings.Locale = event.target.value as string; } - function importSave(): void { - if (window.File && window.FileReader && window.FileList && window.Blob) { - // var fileSelector = clearEventListeners("import-game-file-selector"); - // fileSelector.addEventListener("change", openImportFileHandler, false); - const ii = importInput.current; - if (ii === null) throw new Error("import input should not be null"); - ii.click(); - } else { - dialogBoxCreate("ERR: Your browser does not support HTML5 File API. Cannot import."); - } + function startImport(): void { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return; + const ii = importInput.current; + if (ii === null) throw new Error("import input should not be null"); + ii.click(); } function onImport(event: React.ChangeEvent): void { - props.import(event); + const files = event.target.files; + if (files === null) return; + const file = files[0]; + if (!file) { + dialogBoxCreate("Invalid file selected"); + return; + } + + const reader = new FileReader(); + reader.onload = function (this: FileReader, e: ProgressEvent) { + const target = e.target; + if (target === null) { + console.error("error importing file"); + return; + } + const result = target.result; + if (typeof result !== "string" || result === null) { + console.error("FileReader event was not type string"); + return; + } + const contents = result; + save(contents).then(() => location.reload()); + }; + reader.readAsText(file); } return ( @@ -490,7 +507,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement { import}> -