Various fixes to our upgrade logic (#536)

Stuff broke over time, especially with the major changes we made leading
up to 2.3. We should test with older saves if/when we make large changes
in the future.

Fixes #532
This commit is contained in:
David Walker 2023-05-26 21:16:31 -07:00 committed by GitHub
parent ab207ce36c
commit db26d054fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 54 deletions

@ -238,17 +238,6 @@ export class OfficeSpace {
}
static fromJSON(value: IReviverValue): OfficeSpace {
// Convert employees from the old version
if (Object.hasOwn(value.data, "employees")) {
const empCopy: [{ data: { mor: number; ene: number; exp: number } }] = value.data.employees;
delete value.data.employees;
const ret = Generic_fromJSON(OfficeSpace, value.data);
ret.numEmployees = empCopy.length;
ret.avgMorale = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.numEmployees || 75;
ret.avgEnergy = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.numEmployees || 75;
ret.totalExperience = empCopy.reduce((a, b) => a + b.data.exp, 0);
return ret;
}
return Generic_fromJSON(OfficeSpace, value.data);
}
}

@ -478,6 +478,51 @@ function evaluateVersionCompatibility(ver: string | number): void {
const graft = anyPlayer.graftAugmentationName;
if (graft) Player.augmentations.push({ name: graft, level: 1 });
}
if (ver < 31) {
// This section of 2.3.0 changes is intentionally OUT-OF-ORDER.
// This is because it upgrades how files and paths are handled, and other
// (old) upgrade sections have come to depend on the new format, via
// standard helper functions.
// Other 2.3.0 changes are in their regular place.
Terminal.warn("Migrating to 2.3.0, loading with no scripts.");
const newDirectory = resolveDirectory("v2.3FileChanges/") as Directory;
for (const server of GetAllServers()) {
// Do not load saved scripts on migration
server.savedScripts = [];
let invalidScriptCount = 0;
// There was a brief dev window where Server.scripts was already a map but the filepath changes weren't in yet.
const oldScripts = Array.isArray(server.scripts) ? (server.scripts as Script[]) : [...server.scripts.values()];
server.scripts = new JSONMap();
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
for (const script of oldScripts) {
let newFilePath = resolveScriptFilePath(script.filename);
if (!newFilePath) {
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
script.content = `// Original path: ${script.filename}. Path was no longer valid\n` + script.content;
}
script.filename = newFilePath;
server.scripts.set(newFilePath, script);
}
// Handle changing textFiles to a map as well as FilePath changes at the same time.
if (Array.isArray(server.textFiles)) {
const oldTextFiles = server.textFiles as (TextFile & { fn?: string })[];
server.textFiles = new JSONMap();
let invalidTextCount = 0;
for (const textFile of oldTextFiles) {
const oldName = textFile.fn ?? textFile.filename;
delete textFile.fn;
let newFilePath = resolveTextFilePath(oldName);
if (!newFilePath) {
newFilePath = `${newDirectory}text${++invalidTextCount}.txt` as TextFilePath;
textFile.content = `// Original path: ${textFile.filename}. Path was no longer valid\n` + textFile.content;
}
textFile.filename = newFilePath;
server.textFiles.set(newFilePath, textFile);
}
}
}
}
if (ver < 22) {
v22PlayerBreak();
v2APIBreak();
@ -668,44 +713,6 @@ function evaluateVersionCompatibility(ver: string | number): void {
anyPlayer.lastNodeReset = anyPlayer.lastUpdate - anyPlayer.playtimeSinceLastBitnode;
}
Terminal.warn("Migrating to 2.3.0, loading with no scripts.");
const newDirectory = resolveDirectory("v2.3FileChanges/") as Directory;
for (const server of GetAllServers()) {
// Do not load saved scripts on migration
server.savedScripts = [];
let invalidScriptCount = 0;
// There was a brief dev window where Server.scripts was already a map but the filepath changes weren't in yet.
const oldScripts = Array.isArray(server.scripts) ? (server.scripts as Script[]) : [...server.scripts.values()];
server.scripts = new JSONMap();
// In case somehow there are previously valid filenames that can't be sanitized, they will go in a new directory with a note.
for (const script of oldScripts) {
let newFilePath = resolveScriptFilePath(script.filename);
if (!newFilePath) {
newFilePath = `${newDirectory}script${++invalidScriptCount}.js` as ScriptFilePath;
script.content = `// Original path: ${script.filename}. Path was no longer valid\n` + script.content;
}
script.filename = newFilePath;
server.scripts.set(newFilePath, script);
}
// Handle changing textFiles to a map as well as FilePath changes at the same time.
if (Array.isArray(server.textFiles)) {
const oldTextFiles = server.textFiles as (TextFile & { fn?: string })[];
server.textFiles = new JSONMap();
let invalidTextCount = 0;
for (const textFile of oldTextFiles) {
const oldName = textFile.fn ?? textFile.filename;
delete textFile.fn;
let newFilePath = resolveTextFilePath(oldName);
if (!newFilePath) {
newFilePath = `${newDirectory}text${++invalidTextCount}.txt` as TextFilePath;
textFile.content = `// Original path: ${textFile.filename}. Path was no longer valid\n` + textFile.content;
}
textFile.filename = newFilePath;
server.textFiles.set(newFilePath, textFile);
}
}
}
// Reset corporation to new format.
const oldCorp = anyPlayer.corporation as Corporation | null | 0;
if (oldCorp && Array.isArray(oldCorp.divisions)) {
@ -764,6 +771,7 @@ function loadGame(saveString: string): boolean {
try {
loadStockMarket(saveObj.StockMarketSave);
} catch (e) {
console.error("Couldn't load stock market:", e);
loadStockMarket("");
}
} else {
@ -804,6 +812,7 @@ function loadGame(saveString: string): boolean {
createNewUpdateText();
}
} catch (e) {
console.error("Error upgrading versions:", e);
createNewUpdateText();
}
} else {

@ -1,12 +1,11 @@
import React from "react";
import { SourceFile } from "./SourceFile";
import { initBitNodes, initBitNodeMultipliers } from "../BitNode/BitNode";
import { initBitNodes } from "../BitNode/BitNode";
export const SourceFiles: Record<string, SourceFile> = {};
/** Engine initializer for SourceFiles, BitNodes, and BitNodeMultipliers. Run once at engine load. */
export function initSourceFiles() {
initBitNodes();
initBitNodeMultipliers();
SourceFiles.SourceFile1 = new SourceFile(
1,
(

@ -1,6 +1,7 @@
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { initSourceFiles } from "./SourceFile/SourceFiles";
import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies";
@ -229,11 +230,14 @@ const Engine: {
load: function (saveString) {
startExploits();
setupUncaughtPromiseHandler();
// Source files must be initialized early because save-game translation in
// loadGame() needs them sometimes.
initSourceFiles();
// Load game from save or create new game
if (loadGame(saveString)) {
FormatsNeedToChange.emit();
initSourceFiles();
initBitNodeMultipliers();
initAugmentations(); // Also calls Player.reapplyAllAugmentations()
Player.reapplyAllSourceFiles();
if (Player.hasWseAccount) {
@ -374,7 +378,7 @@ const Engine: {
} else {
// No save found, start new game
FormatsNeedToChange.emit();
initSourceFiles();
initBitNodeMultipliers();
Engine.start(); // Run main game loop and Scripts loop
Player.init();
initForeignServers(Player.getHomeComputer());

@ -34,6 +34,9 @@ export function Reviver(_key: string, value: unknown): any {
case "Industry":
console.warn("Converting a corp from pre-2.3");
return value.data; // Will immediately be overwritten by v2.3 save migration code
case "Employee":
console.warn("Converting a corp from pre-2.2");
return value.data; // Will immediately be overwritten by v2.3 save migration code
}
// Missing constructor with no special handling. Throw error.
throw new Error(`Could not locate constructor named ${value.ctor}. If the save data is valid, this is a bug.`);

@ -229,7 +229,7 @@ export const v2APIBreak = () => {
}
// API break function is called before the version31 2.3.0 changes, scripts are still an array.
for (const script of home.scripts as unknown as Script[]) {
for (const script of home.scripts.values() as unknown as Script[]) {
processScript(rules, script);
}