MISC: Support compression of save data (#1162)

* Use Compression Streams API instead of jszip or other libraries.
* Remove usage of base64 in the new binary format.
* Do not convert binary data to string and back. The type of save data is SaveData, it's either string (old base64 format) or Uint8Array (new binary format).
* Proper support for interacting with electron-related code. Electron-related code assumes that save data is in the base64 format.
* Proper support for other tools (DevMenu, pretty-save.js). Full support for DevMenu will be added in a follow-up PR. Check the comments in src\DevMenu\ui\SaveFileDev.tsx for details.
This commit is contained in:
catloversg
2024-03-28 11:08:09 +07:00
committed by GitHub
parent 75dabd10be
commit 8553bcb8fc
22 changed files with 358 additions and 289 deletions
+7 -6
View File
@@ -38,6 +38,7 @@ import { Page } from "../../Router";
import { useBoolean } from "../hooks";
import { ComparisonIcon } from "./ComparisonIcon";
import { SaveData } from "../../../types";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -89,7 +90,7 @@ const playerSkills: (keyof Skills)[] = ["hacking", "strength", "defense", "dexte
let initialAutosave = 0;
export const ImportSave = (props: { importString: string; automatic: boolean }): JSX.Element => {
export const ImportSave = (props: { saveData: SaveData; automatic: boolean }): JSX.Element => {
const classes = useStyles();
const [importData, setImportData] = useState<ImportData | undefined>();
const [currentData, setCurrentData] = useState<ImportData | undefined>();
@@ -105,7 +106,7 @@ export const ImportSave = (props: { importString: string; automatic: boolean }):
};
const handleImport = async (): Promise<void> => {
await saveObject.importGame(props.importString, true);
await saveObject.importGame(props.saveData, true);
pushImportResult(true);
};
@@ -122,16 +123,16 @@ export const ImportSave = (props: { importString: string; automatic: boolean }):
useEffect(() => {
async function fetchData(): Promise<void> {
const dataBeingImported = await saveObject.getImportDataFromString(props.importString);
const dataCurrentlyInGame = await saveObject.getImportDataFromString(saveObject.getSaveString(true));
const dataBeingImported = await saveObject.getImportDataFromSaveData(props.saveData);
const dataCurrentlyInGame = await saveObject.getImportDataFromSaveData(await saveObject.getSaveData(true));
setImportData(dataBeingImported);
setCurrentData(dataCurrentlyInGame);
return Promise.resolve();
}
if (props.importString) fetchData();
}, [props.importString]);
if (props.saveData) fetchData();
}, [props.saveData]);
if (!importData || !currentData) return <></>;
+53 -24
View File
@@ -12,11 +12,15 @@ import { SoftResetButton } from "./SoftResetButton";
import DirectionsRunIcon from "@mui/icons-material/DirectionsRun";
import GitHubIcon from "@mui/icons-material/GitHub";
import { isBinaryFormat } from "../../../electron/saveDataBinaryFormat";
import { InvalidSaveData, UnsupportedSaveData } from "../../utils/SaveDataUtils";
export let RecoveryMode = false;
let sourceError: unknown;
export function ActivateRecoveryMode(): void {
export function ActivateRecoveryMode(error: unknown): void {
RecoveryMode = true;
sourceError = error;
}
interface IProps {
@@ -29,6 +33,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
function recover(): void {
if (resetError) resetError();
RecoveryMode = false;
sourceError = undefined;
Router.toPage(Page.Terminal);
}
Settings.AutosaveInterval = 0;
@@ -37,41 +42,65 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
load()
.then((content) => {
const epochTime = Math.round(Date.now() / 1000);
const filename = `RECOVERY_BITBURNER_${epochTime}.json`;
const extension = isBinaryFormat(content) ? "json.gz" : "json";
const filename = `RECOVERY_BITBURNER_${epochTime}.${extension}`;
download(filename, content);
})
.catch((err) => console.error(err));
}, []);
let instructions;
if (sourceError instanceof UnsupportedSaveData) {
instructions = <Typography variant="h6">Please update your browser.</Typography>;
} else if (sourceError instanceof InvalidSaveData) {
instructions = (
<Typography variant="h6">Your save data is invalid. Please import a valid backup save file.</Typography>
);
} else {
instructions = (
<Box>
<Typography>It is recommended to alert a developer.</Typography>
<Typography>
<Link href={errorData?.issueUrl ?? newIssueUrl} target="_blank">
File an issue on github
</Link>
</Typography>
<Typography>
<Link href="https://www.reddit.com/r/Bitburner/" target="_blank">
Make a reddit post
</Link>
</Typography>
<Typography>
<Link href="https://discord.gg/TFc3hKD" target="_blank">
Post in the #bug-report channel on Discord.
</Link>
</Typography>
<Typography>Please include your save file.</Typography>
</Box>
);
}
return (
<Box sx={{ padding: "8px 16px", minHeight: "100vh", maxWidth: "1200px", boxSizing: "border-box" }}>
<Typography variant="h3">RECOVERY MODE ACTIVATED</Typography>
<Typography>
There was an error with your save file and the game went into recovery mode. In this mode saving is disabled and
the game will automatically export your save file (to prevent corruption).
There was an error with your save file and the game went into recovery mode. In this mode, saving is disabled
and the game will automatically export your save file to prevent corruption.
</Typography>
<Typography>At this point it is recommended to alert a developer.</Typography>
<Typography>
<Link href={errorData?.issueUrl ?? newIssueUrl} target="_blank">
File an issue on github
</Link>
</Typography>
<Typography>
<Link href="https://www.reddit.com/r/Bitburner/" target="_blank">
Make a reddit post
</Link>
</Typography>
<Typography>
<Link href="https://discord.gg/TFc3hKD" target="_blank">
Post in the #bug-report channel on Discord.
</Link>
</Typography>
<Typography>Please include your save file.</Typography>
<br />
{sourceError && (
<Box>
<Typography variant="h6" color={Settings.theme.error}>
Error: {sourceError.toString()}
</Typography>
<br />
</Box>
)}
{instructions}
<br />
<Typography>You can disable recovery mode now. But chances are the game will not work correctly.</Typography>
<Typography>You can disable the recovery mode, but the game may not work correctly.</Typography>
<ButtonGroup sx={{ my: 2 }}>
<Tooltip title="Disables the recovery mode & attempt to head back to the terminal page. This may or may not work. Ensure you have saved the recovery file.">
<Tooltip title="Disable the recovery mode and attempt to head back to the terminal page. This may or may not work. Ensure you saved the recovery file.">
<Button onClick={recover} startIcon={<DirectionsRunIcon />}>
Disable Recovery Mode
</Button>
@@ -96,7 +125,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
sx={{ "& .MuiOutlinedInput-root": { color: Settings.theme.secondary } }}
/>
</Box>
<Tooltip title="Submitting an issue to GitHub really help us improve the game!">
<Tooltip title="Submitting an issue to GitHub really helps us improve the game!">
<Button
component={Link}
startIcon={<GitHubIcon />}