mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-26 19:14:32 +02:00
UI: Add option to enable/disable syncing Steam achievements (#2117)
This commit is contained in:
+92
-24
@@ -65,6 +65,7 @@ export interface ImportPlayerData {
|
||||
bitNodeLevel: number;
|
||||
sourceFiles: number;
|
||||
exploits: number;
|
||||
syncSteamAchievements: boolean;
|
||||
}
|
||||
|
||||
export type BitburnerSaveObjectType = {
|
||||
@@ -83,6 +84,13 @@ export type BitburnerSaveObjectType = {
|
||||
GoSave: unknown; // "loadGo" function can process unknown data
|
||||
};
|
||||
|
||||
type ParsedSaveData = {
|
||||
data: {
|
||||
PlayerSave: string;
|
||||
SettingsSave: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* This function asserts the unknown saveObject.
|
||||
*
|
||||
@@ -136,6 +144,18 @@ function assertBitburnerSaveObjectType(saveObject: unknown): asserts saveObject
|
||||
}
|
||||
}
|
||||
|
||||
function assertParsedSaveData(parsedSaveData: unknown): asserts parsedSaveData is ParsedSaveData {
|
||||
if (
|
||||
!isObject(parsedSaveData) ||
|
||||
parsedSaveData.ctor !== "BitburnerSaveObject" ||
|
||||
!isObject(parsedSaveData.data) ||
|
||||
typeof parsedSaveData.data.PlayerSave !== "string"
|
||||
) {
|
||||
console.error("parsedSaveData:", parsedSaveData);
|
||||
throw new Error("The parsed save data is not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
PlayerSave = "";
|
||||
AllServersSave = "";
|
||||
@@ -230,9 +250,37 @@ class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
downloadContentAsFile(saveData, filename);
|
||||
}
|
||||
|
||||
async importGame(saveData: SaveData, reload = true): Promise<void> {
|
||||
async importGame(
|
||||
saveData: SaveData,
|
||||
overrideSettings?: {
|
||||
SyncSteamAchievements: boolean;
|
||||
},
|
||||
): Promise<void> {
|
||||
if (!saveData || saveData.length === 0) {
|
||||
throw new Error("Invalid import string");
|
||||
dialogBoxCreate("Invalid save data");
|
||||
return;
|
||||
}
|
||||
// Modify settings in save data if needed (i.e., toggle SyncSteamAchievements before importing).
|
||||
if (overrideSettings) {
|
||||
let parsedSaveData;
|
||||
try {
|
||||
parsedSaveData = await this.getParsedSaveData(saveData);
|
||||
// Validate SettingsSave
|
||||
if (parsedSaveData.data.SettingsSave && typeof parsedSaveData.data.SettingsSave === "string") {
|
||||
// Parse settings from data.SettingsSave
|
||||
const settings: unknown = JSON.parse(parsedSaveData.data.SettingsSave);
|
||||
assertObject(settings);
|
||||
// Modify setting
|
||||
settings.SyncSteamAchievements = overrideSettings.SyncSteamAchievements;
|
||||
// Save modified data back to saveData
|
||||
parsedSaveData.data.SettingsSave = JSON.stringify(settings);
|
||||
saveData = await encodeJsonSaveString(JSON.stringify(parsedSaveData));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
dialogBoxCreate(`Cannot override settings: ${error}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await save(saveData);
|
||||
@@ -246,58 +294,62 @@ class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
dialogBoxCreate(`Cannot import save data: ${error}`);
|
||||
return;
|
||||
}
|
||||
if (reload) {
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
}
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
}
|
||||
|
||||
async getSaveDataFromFile(files: FileList | null): Promise<SaveData> {
|
||||
if (files === null) return Promise.reject(new Error("No file selected"));
|
||||
if (files === null) {
|
||||
throw new Error("No file selected");
|
||||
}
|
||||
const file = files[0];
|
||||
if (!file) return Promise.reject(new Error("Invalid file selected"));
|
||||
if (!file) {
|
||||
throw new Error("Invalid file selected");
|
||||
}
|
||||
|
||||
const rawData = new Uint8Array(await file.arrayBuffer());
|
||||
if (isBinaryFormat(rawData)) {
|
||||
return rawData;
|
||||
} else {
|
||||
return new TextDecoder().decode(rawData);
|
||||
}
|
||||
return new TextDecoder().decode(rawData);
|
||||
}
|
||||
|
||||
async getImportDataFromSaveData(saveData: SaveData): Promise<ImportData> {
|
||||
if (!saveData || saveData.length === 0) throw new Error("Invalid save data");
|
||||
async getParsedSaveData(saveData: SaveData): Promise<ParsedSaveData> {
|
||||
if (!saveData || saveData.length === 0) {
|
||||
throw new Error("Invalid save data");
|
||||
}
|
||||
|
||||
let decodedSaveData;
|
||||
try {
|
||||
decodedSaveData = await decodeSaveData(saveData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// Rethrow immediately if the error is SaveDataError; otherwise, handle it below.
|
||||
if (error instanceof SaveDataError) {
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!decodedSaveData || decodedSaveData === "") {
|
||||
console.error("decodedSaveData:", decodedSaveData);
|
||||
return Promise.reject(new Error("Save game is invalid. The save data cannot be decoded."));
|
||||
console.error("saveData:", saveData);
|
||||
throw new Error("The save data cannot be decoded.");
|
||||
}
|
||||
|
||||
let parsedSaveData: unknown;
|
||||
try {
|
||||
parsedSaveData = JSON.parse(decodedSaveData);
|
||||
} catch (error) {
|
||||
console.error(error); // We'll handle below
|
||||
console.error("decodedSaveData:", decodedSaveData);
|
||||
throw new Error("The decoded save data is not valid.");
|
||||
}
|
||||
|
||||
if (
|
||||
!isObject(parsedSaveData) ||
|
||||
parsedSaveData.ctor !== "BitburnerSaveObject" ||
|
||||
!isObject(parsedSaveData.data) ||
|
||||
typeof parsedSaveData.data.PlayerSave !== "string"
|
||||
) {
|
||||
console.error("decodedSaveData:", decodedSaveData);
|
||||
return Promise.reject(new Error("Save game is invalid. The decoded save data is not valid."));
|
||||
}
|
||||
assertParsedSaveData(parsedSaveData);
|
||||
|
||||
return parsedSaveData;
|
||||
}
|
||||
|
||||
async getImportDataFromSaveData(saveData: SaveData): Promise<ImportData> {
|
||||
const parsedSaveData = await this.getParsedSaveData(saveData);
|
||||
|
||||
const data: ImportData = {
|
||||
saveData: saveData,
|
||||
@@ -305,6 +357,20 @@ class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
|
||||
const importedPlayer = loadPlayer(parsedSaveData.data.PlayerSave);
|
||||
|
||||
let syncSteamAchievements = true;
|
||||
// Parse data.SettingsSave to get syncSteamAchievements.
|
||||
if (parsedSaveData.data.SettingsSave && typeof parsedSaveData.data.SettingsSave === "string") {
|
||||
try {
|
||||
const settings: unknown = JSON.parse(parsedSaveData.data.SettingsSave);
|
||||
assertObject(settings);
|
||||
if (typeof settings.SyncSteamAchievements === "boolean") {
|
||||
syncSteamAchievements = settings.SyncSteamAchievements;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
const playerData: ImportPlayerData = {
|
||||
identifier: importedPlayer.identifier,
|
||||
lastSave: importedPlayer.lastSave,
|
||||
@@ -321,10 +387,12 @@ class BitburnerSaveObject implements BitburnerSaveObjectType {
|
||||
bitNodeLevel: importedPlayer.sourceFileLvl(Player.bitNodeN) + 1,
|
||||
sourceFiles: [...importedPlayer.sourceFiles].reduce<number>((total, [__bn, lvl]) => (total += lvl), 0),
|
||||
exploits: importedPlayer.exploits.length,
|
||||
|
||||
syncSteamAchievements,
|
||||
};
|
||||
|
||||
data.playerData = playerData;
|
||||
return Promise.resolve(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
|
||||
Reference in New Issue
Block a user