import { loadAliases, loadGlobalAliases, Aliases, GlobalAliases } from "./Alias"; import { Companies, loadCompanies } from "./Company/Companies"; import { CONSTANTS } from "./Constants"; import { Factions, loadFactions } from "./Faction/Factions"; import { loadAllGangs, AllGangs } from "./Gang/AllGangs"; import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers"; import { Player, loadPlayer } from "./Player"; import { saveAllServers, loadAllServers } from "./Server/AllServers"; import { Settings } from "./Settings/Settings"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket"; import { GameSavedEvents } from "./ui/React/Snackbar"; import * as ExportBonus from "./ExportBonus"; import { dialogBoxCreate } from "./ui/React/DialogBox"; import { Reviver, Generic_toJSON, Generic_fromJSON } from "./utils/JSONReviver"; import { save } from "./db"; /* SaveObject.js * Defines the object used to save/load games */ class BitburnerSaveObject { PlayerSave = ""; AllServersSave = ""; CompaniesSave = ""; FactionsSave = ""; AliasesSave = ""; GlobalAliasesSave = ""; MessagesSave = ""; StockMarketSave = ""; SettingsSave = ""; VersionSave = ""; AllGangsSave = ""; LastExportBonus = ""; getSaveString(): string { this.PlayerSave = JSON.stringify(Player); this.AllServersSave = saveAllServers(); this.CompaniesSave = JSON.stringify(Companies); this.FactionsSave = JSON.stringify(Factions); this.AliasesSave = JSON.stringify(Aliases); this.GlobalAliasesSave = JSON.stringify(GlobalAliases); this.MessagesSave = JSON.stringify(Messages); this.StockMarketSave = JSON.stringify(StockMarket); this.SettingsSave = JSON.stringify(Settings); this.VersionSave = JSON.stringify(CONSTANTS.Version); this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus); if (Player.inGang()) { this.AllGangsSave = JSON.stringify(AllGangs); } const saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this)))); return saveString; } saveGame(): void { const saveString = this.getSaveString(); save(saveString) .then(() => GameSavedEvents.emit()) .catch((err) => console.error(err)); } exportGame(): void { const saveString = this.getSaveString(); // Save file name is based on current timestamp and BitNode const epochTime = Math.round(Date.now() / 1000); const bn = Player.bitNodeN; const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`; const file = new Blob([saveString], { type: "text/plain" }); if (window.navigator.msSaveOrOpenBlob) { // IE10+ window.navigator.msSaveOrOpenBlob(file, filename); } else { // Others const a = document.createElement("a"), url = URL.createObjectURL(file); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(function () { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } } toJSON(): any { return Generic_toJSON("BitburnerSaveObject", this); } static fromJSON(value: { data: any }): BitburnerSaveObject { return Generic_fromJSON(BitburnerSaveObject, value.data); } } // Makes necessary changes to the loaded/imported data to ensure // the game stills works with new versions function evaluateVersionCompatibility(ver: string): void { // We have to do this because ts won't let us otherwise const anyPlayer = Player as any; // This version refactored the Company/job-related code if (ver <= "0.41.2") { // Player's company position is now a string if (anyPlayer.companyPosition != null && typeof anyPlayer.companyPosition !== "string") { anyPlayer.companyPosition = anyPlayer.companyPosition.data.positionName; if (anyPlayer.companyPosition == null) { anyPlayer.companyPosition = ""; } } // The "companyName" property of all Companies is renamed to "name" for (const companyName in Companies) { const company: any = Companies[companyName]; if (company.name == 0 && company.companyName != null) { company.name = company.companyName; } if (company.companyPositions instanceof Array) { const pos: any = {}; for (let i = 0; i < company.companyPositions.length; ++i) { pos[company.companyPositions[i]] = true; } company.companyPositions = pos; } } } // This version allowed players to hold multiple jobs if (ver < "0.43.0") { if (anyPlayer.companyName !== "" && anyPlayer.companyPosition != null && anyPlayer.companyPosition !== "") { anyPlayer.jobs[anyPlayer.companyName] = anyPlayer.companyPosition; } delete anyPlayer.companyPosition; } } function loadGame(saveString: string): boolean { if (!saveString) return false; saveString = decodeURIComponent(escape(atob(saveString))); const saveObj = JSON.parse(saveString, Reviver); loadPlayer(saveObj.PlayerSave); loadAllServers(saveObj.AllServersSave); loadCompanies(saveObj.CompaniesSave); loadFactions(saveObj.FactionsSave); if (saveObj.hasOwnProperty("AliasesSave")) { try { loadAliases(saveObj.AliasesSave); } catch (e) { console.warn(`Could not load Aliases from save`); loadAliases(""); } } else { console.warn(`Save file did not contain an Aliases property`); loadAliases(""); } if (saveObj.hasOwnProperty("GlobalAliasesSave")) { try { loadGlobalAliases(saveObj.GlobalAliasesSave); } catch (e) { console.warn(`Could not load GlobalAliases from save`); loadGlobalAliases(""); } } else { console.warn(`Save file did not contain a GlobalAliases property`); loadGlobalAliases(""); } if (saveObj.hasOwnProperty("MessagesSave")) { try { loadMessages(saveObj.MessagesSave); } catch (e) { console.warn(`Could not load Messages from save`); initMessages(); } } else { console.warn(`Save file did not contain a Messages property`); 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) { console.error("ERROR: Failed to parse Settings. Re-initing default values"); Settings.init(); } } else { Settings.init(); } if (saveObj.hasOwnProperty("LastExportBonus")) { try { 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 (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) { try { loadAllGangs(saveObj.AllGangsSave); } catch (e) { console.error("ERROR: Failed to parse AllGangsSave: " + e); } } if (saveObj.hasOwnProperty("VersionSave")) { try { const ver = JSON.parse(saveObj.VersionSave, Reviver); evaluateVersionCompatibility(ver); if (window.location.href.toLowerCase().includes("bitburner-beta")) { // Beta branch, always show changes createBetaUpdateText(); } else if (ver != CONSTANTS.Version) { createNewUpdateText(); } } catch (e) { createNewUpdateText(); } } else { createNewUpdateText(); } return true; } function createNewUpdateText(): void { dialogBoxCreate( "New update!
" + "Please report any bugs/issues through the github repository " + "or the Bitburner subreddit (reddit.com/r/bitburner).

" + CONSTANTS.LatestUpdate, ); } function createBetaUpdateText(): void { dialogBoxCreate( "You are playing on the beta environment! This branch of the game " + "features the latest developments in the game. This version may be unstable.
" + "Please report any bugs/issues through the github repository (https://github.com/danielyxie/bitburner/issues) " + "or the Bitburner subreddit (reddit.com/r/bitburner).

" + CONSTANTS.LatestUpdate, ); } Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject; export { saveObject, loadGame }; const saveObject = new BitburnerSaveObject();