1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-05 17:14:03 +00:00

Migrate old savegames

This commit is contained in:
tobspr 2020-09-22 09:32:31 +02:00
parent 665360c5a5
commit 9075841768
15 changed files with 1243 additions and 958 deletions

View File

@ -520,7 +520,7 @@
"spriteSourceSize": {"x":1,"y":0,"w":143,"h":144}, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":144},
"sourceSize": {"w":144,"h":144} "sourceSize": {"w":144,"h":144}
}, },
"sprites/blueprints/rotater-fl.png": "sprites/blueprints/rotater-rotate180.png":
{ {
"frame": {"x":423,"y":1591,"w":142,"h":144}, "frame": {"x":423,"y":1591,"w":142,"h":144},
"rotated": false, "rotated": false,
@ -928,7 +928,7 @@
"spriteSourceSize": {"x":2,"y":0,"w":141,"h":143}, "spriteSourceSize": {"x":2,"y":0,"w":141,"h":143},
"sourceSize": {"w":144,"h":144} "sourceSize": {"w":144,"h":144}
}, },
"sprites/buildings/rotater-fl.png": "sprites/buildings/rotater-rotate180.png":
{ {
"frame": {"x":1611,"y":612,"w":141,"h":143}, "frame": {"x":1611,"y":612,"w":141,"h":143},
"rotated": false, "rotated": false,
@ -1487,6 +1487,6 @@
"format": "RGBA8888", "format": "RGBA8888",
"size": {"w":2048,"h":2048}, "size": {"w":2048,"h":2048},
"scale": "0.75", "scale": "0.75",
"smartupdate": "$TexturePacker:SmartUpdate:c38df9b4e442ab7ca6aca0b780d0839e:99e8394e24df838bd0f576e1caf16c3d:908b89f5ca8ff73e331a35a3b14d0604$" "smartupdate": "$TexturePacker:SmartUpdate:f21861f754636ac30e87fdbab4b77dbd:161fc7d539e5ffa693c8470dcc931f75:908b89f5ca8ff73e331a35a3b14d0604$"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -520,7 +520,7 @@
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48} "sourceSize": {"w":48,"h":48}
}, },
"sprites/blueprints/rotater-fl.png": "sprites/blueprints/rotater-rotate180.png":
{ {
"frame": {"x":965,"y":165,"w":48,"h":48}, "frame": {"x":965,"y":165,"w":48,"h":48},
"rotated": false, "rotated": false,
@ -928,7 +928,7 @@
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48} "sourceSize": {"w":48,"h":48}
}, },
"sprites/buildings/rotater-fl.png": "sprites/buildings/rotater-rotate180.png":
{ {
"frame": {"x":397,"y":254,"w":48,"h":48}, "frame": {"x":397,"y":254,"w":48,"h":48},
"rotated": false, "rotated": false,
@ -1487,6 +1487,6 @@
"format": "RGBA8888", "format": "RGBA8888",
"size": {"w":1024,"h":1024}, "size": {"w":1024,"h":1024},
"scale": "0.25", "scale": "0.25",
"smartupdate": "$TexturePacker:SmartUpdate:c38df9b4e442ab7ca6aca0b780d0839e:99e8394e24df838bd0f576e1caf16c3d:908b89f5ca8ff73e331a35a3b14d0604$" "smartupdate": "$TexturePacker:SmartUpdate:f21861f754636ac30e87fdbab4b77dbd:161fc7d539e5ffa693c8470dcc931f75:908b89f5ca8ff73e331a35a3b14d0604$"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

View File

@ -520,7 +520,7 @@
"spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96},
"sourceSize": {"w":96,"h":96} "sourceSize": {"w":96,"h":96}
}, },
"sprites/blueprints/rotater-fl.png": "sprites/blueprints/rotater-rotate180.png":
{ {
"frame": {"x":525,"y":1100,"w":95,"h":96}, "frame": {"x":525,"y":1100,"w":95,"h":96},
"rotated": false, "rotated": false,
@ -928,7 +928,7 @@
"spriteSourceSize": {"x":1,"y":0,"w":95,"h":96}, "spriteSourceSize": {"x":1,"y":0,"w":95,"h":96},
"sourceSize": {"w":96,"h":96} "sourceSize": {"w":96,"h":96}
}, },
"sprites/buildings/rotater-fl.png": "sprites/buildings/rotater-rotate180.png":
{ {
"frame": {"x":213,"y":1234,"w":95,"h":96}, "frame": {"x":213,"y":1234,"w":95,"h":96},
"rotated": false, "rotated": false,
@ -1487,6 +1487,6 @@
"format": "RGBA8888", "format": "RGBA8888",
"size": {"w":1024,"h":2048}, "size": {"w":1024,"h":2048},
"scale": "0.5", "scale": "0.5",
"smartupdate": "$TexturePacker:SmartUpdate:c38df9b4e442ab7ca6aca0b780d0839e:99e8394e24df838bd0f576e1caf16c3d:908b89f5ca8ff73e331a35a3b14d0604$" "smartupdate": "$TexturePacker:SmartUpdate:f21861f754636ac30e87fdbab4b77dbd:161fc7d539e5ffa693c8470dcc931f75:908b89f5ca8ff73e331a35a3b14d0604$"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 KiB

After

Width:  |  Height:  |  Size: 705 KiB

View File

@ -86,7 +86,10 @@ export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant)
const hash = metaBuilding.getId() + "/" + variant + "/" + rotationVariant; const hash = metaBuilding.getId() + "/" + variant + "/" + rotationVariant;
const result = variantsCache.get(hash); const result = variantsCache.get(hash);
if (G_IS_DEV) { if (G_IS_DEV) {
assertAlways(!!result, "Building not found by data: " + hash); if (!result) {
console.warn("Known hashes:", Array.from(variantsCache.keys()));
assertAlways(false, "Building not found by data: " + hash);
}
} }
return result; return result;
} }

View File

@ -16,8 +16,6 @@ export const beltOverlayMatrices = {
[enumDirection.right]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]), [enumDirection.right]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]),
}; };
export class MetaBeltBaseBuilding extends MetaBuilding {}
export class MetaBeltBuilding extends MetaBuilding { export class MetaBeltBuilding extends MetaBuilding {
constructor() { constructor() {
super("belt"); super("belt");

View File

@ -68,6 +68,10 @@ export class HUDEntityDebugger extends BaseHUDPart {
* @param {Array} recursion * @param {Array} recursion
*/ */
propertyToHTML(name, val, indent = 0, recursion = []) { propertyToHTML(name, val, indent = 0, recursion = []) {
if (indent > 20) {
return;
}
if (val !== null && typeof val === "object") { if (val !== null && typeof val === "object") {
// Array is displayed like object, with indexes // Array is displayed like object, with indexes
recursion.push(val); recursion.push(val);

File diff suppressed because it is too large Load Diff

View File

@ -1,273 +1,279 @@
import { ReadWriteProxy } from "../core/read_write_proxy"; import { ReadWriteProxy } from "../core/read_write_proxy";
import { ExplainedResult } from "../core/explained_result"; import { ExplainedResult } from "../core/explained_result";
import { SavegameSerializer } from "./savegame_serializer"; import { SavegameSerializer } from "./savegame_serializer";
import { BaseSavegameInterface } from "./savegame_interface"; import { BaseSavegameInterface } from "./savegame_interface";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { getSavegameInterface, savegameInterfaces } from "./savegame_interface_registry"; import { getSavegameInterface, savegameInterfaces } from "./savegame_interface_registry";
import { SavegameInterface_V1001 } from "./schemas/1001"; import { SavegameInterface_V1001 } from "./schemas/1001";
import { SavegameInterface_V1002 } from "./schemas/1002"; import { SavegameInterface_V1002 } from "./schemas/1002";
import { SavegameInterface_V1003 } from "./schemas/1003"; import { SavegameInterface_V1003 } from "./schemas/1003";
import { SavegameInterface_V1004 } from "./schemas/1004"; import { SavegameInterface_V1004 } from "./schemas/1004";
import { SavegameInterface_V1005 } from "./schemas/1005"; import { SavegameInterface_V1005 } from "./schemas/1005";
import { SavegameInterface_V1006 } from "./schemas/1006";
const logger = createLogger("savegame");
const logger = createLogger("savegame");
/**
* @typedef {import("../application").Application} Application /**
* @typedef {import("../game/root").GameRoot} GameRoot * @typedef {import("../application").Application} Application
* @typedef {import("./savegame_typedefs").SavegameData} SavegameData * @typedef {import("../game/root").GameRoot} GameRoot
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata * @typedef {import("./savegame_typedefs").SavegameData} SavegameData
* @typedef {import("./savegame_typedefs").SavegameStats} SavegameStats * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
* @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame * @typedef {import("./savegame_typedefs").SavegameStats} SavegameStats
*/ * @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame
*/
export class Savegame extends ReadWriteProxy {
/** export class Savegame extends ReadWriteProxy {
* /**
* @param {Application} app *
* @param {object} param0 * @param {Application} app
* @param {string} param0.internalId * @param {object} param0
* @param {SavegameMetadata} param0.metaDataRef Handle to the meta data * @param {string} param0.internalId
*/ * @param {SavegameMetadata} param0.metaDataRef Handle to the meta data
constructor(app, { internalId, metaDataRef }) { */
super(app, "savegame-" + internalId + ".bin"); constructor(app, { internalId, metaDataRef }) {
this.internalId = internalId; super(app, "savegame-" + internalId + ".bin");
this.metaDataRef = metaDataRef; this.internalId = internalId;
this.metaDataRef = metaDataRef;
/** @type {SavegameData} */
this.currentData = this.getDefaultData(); /** @type {SavegameData} */
this.currentData = this.getDefaultData();
assert(
savegameInterfaces[Savegame.getCurrentVersion()], assert(
"Savegame interface not defined: " + Savegame.getCurrentVersion() savegameInterfaces[Savegame.getCurrentVersion()],
); "Savegame interface not defined: " + Savegame.getCurrentVersion()
} );
}
//////// RW Proxy Impl //////////
//////// RW Proxy Impl //////////
/**
* @returns {number} /**
*/ * @returns {number}
static getCurrentVersion() { */
return 1005; static getCurrentVersion() {
} return 1006;
}
/**
* @returns {typeof BaseSavegameInterface} /**
*/ * @returns {typeof BaseSavegameInterface}
static getReaderClass() { */
return savegameInterfaces[Savegame.getCurrentVersion()]; static getReaderClass() {
} return savegameInterfaces[Savegame.getCurrentVersion()];
}
/**
* @returns {number} /**
*/ * @returns {number}
getCurrentVersion() { */
return /** @type {typeof Savegame} */ (this.constructor).getCurrentVersion(); getCurrentVersion() {
} return /** @type {typeof Savegame} */ (this.constructor).getCurrentVersion();
}
/**
* Returns the savegames default data /**
* @returns {SavegameData} * Returns the savegames default data
*/ * @returns {SavegameData}
getDefaultData() { */
return { getDefaultData() {
version: this.getCurrentVersion(), return {
dump: null, version: this.getCurrentVersion(),
stats: {}, dump: null,
lastUpdate: Date.now(), stats: {},
}; lastUpdate: Date.now(),
} };
}
/**
* Migrates the savegames data /**
* @param {SavegameData} data * Migrates the savegames data
*/ * @param {SavegameData} data
migrate(data) { */
if (data.version < 1000) { migrate(data) {
return ExplainedResult.bad("Can not migrate savegame, too old"); if (data.version < 1000) {
} return ExplainedResult.bad("Can not migrate savegame, too old");
}
if (data.version === 1000) {
SavegameInterface_V1001.migrate1000to1001(data); if (data.version === 1000) {
data.version = 1001; SavegameInterface_V1001.migrate1000to1001(data);
} data.version = 1001;
}
if (data.version === 1001) {
SavegameInterface_V1002.migrate1001to1002(data); if (data.version === 1001) {
data.version = 1002; SavegameInterface_V1002.migrate1001to1002(data);
} data.version = 1002;
}
if (data.version === 1002) {
SavegameInterface_V1003.migrate1002to1003(data); if (data.version === 1002) {
data.version = 1003; SavegameInterface_V1003.migrate1002to1003(data);
} data.version = 1003;
}
if (data.version === 1003) {
SavegameInterface_V1004.migrate1003to1004(data); if (data.version === 1003) {
data.version = 1004; SavegameInterface_V1004.migrate1003to1004(data);
} data.version = 1004;
}
if (data.version === 1004) {
SavegameInterface_V1005.migrate1004to1005(data); if (data.version === 1004) {
data.version = 1005; SavegameInterface_V1005.migrate1004to1005(data);
} data.version = 1005;
}
return ExplainedResult.good();
} if (data.version === 1005) {
SavegameInterface_V1006.migrate1005to1006(data);
/** data.version = 1006;
* Verifies the savegames data }
* @param {SavegameData} data
*/ return ExplainedResult.good();
verify(data) { }
if (!data.dump) {
// Well, guess that works /**
return ExplainedResult.good(); * Verifies the savegames data
} * @param {SavegameData} data
*/
if (!this.getDumpReaderForExternalData(data).validate()) { verify(data) {
return ExplainedResult.bad("dump-reader-failed-validation"); if (!data.dump) {
} // Well, guess that works
return ExplainedResult.good(); return ExplainedResult.good();
} }
//////// Subclasses interface //////// if (!this.getDumpReaderForExternalData(data).validate()) {
return ExplainedResult.bad("dump-reader-failed-validation");
/** }
* Returns if this game can be saved on disc return ExplainedResult.good();
* @returns {boolean} }
*/
isSaveable() { //////// Subclasses interface ////////
return true;
} /**
/** * Returns if this game can be saved on disc
* Returns the statistics of the savegame * @returns {boolean}
* @returns {SavegameStats} */
*/ isSaveable() {
getStatistics() { return true;
return this.currentData.stats; }
} /**
* Returns the statistics of the savegame
/** * @returns {SavegameStats}
* Returns the *real* last update of the savegame, not the one of the metadata */
* which could also be the servers one getStatistics() {
*/ return this.currentData.stats;
getRealLastUpdate() { }
return this.currentData.lastUpdate;
} /**
* Returns the *real* last update of the savegame, not the one of the metadata
/** * which could also be the servers one
* Returns if this game has a serialized game dump */
*/ getRealLastUpdate() {
hasGameDump() { return this.currentData.lastUpdate;
return !!this.currentData.dump && this.currentData.dump.entities.length > 0; }
}
/**
/** * Returns if this game has a serialized game dump
* Returns the current game dump */
* @returns {SerializedGame} hasGameDump() {
*/ return !!this.currentData.dump && this.currentData.dump.entities.length > 0;
getCurrentDump() { }
return this.currentData.dump;
} /**
* Returns the current game dump
/** * @returns {SerializedGame}
* Returns a reader to access the data */
* @returns {BaseSavegameInterface} getCurrentDump() {
*/ return this.currentData.dump;
getDumpReader() { }
if (!this.currentData.dump) {
logger.warn("Getting reader on null-savegame dump"); /**
} * Returns a reader to access the data
* @returns {BaseSavegameInterface}
const cls = /** @type {typeof Savegame} */ (this.constructor).getReaderClass(); */
return new cls(this.currentData); getDumpReader() {
} if (!this.currentData.dump) {
logger.warn("Getting reader on null-savegame dump");
/** }
* Returns a reader to access external data
* @returns {BaseSavegameInterface} const cls = /** @type {typeof Savegame} */ (this.constructor).getReaderClass();
*/ return new cls(this.currentData);
getDumpReaderForExternalData(data) { }
assert(data.version, "External data contains no version");
return getSavegameInterface(data); /**
} * Returns a reader to access external data
* @returns {BaseSavegameInterface}
///////// Public Interface /////////// */
getDumpReaderForExternalData(data) {
/** assert(data.version, "External data contains no version");
* Updates the last update field so we can send the savegame to the server, return getSavegameInterface(data);
* WITHOUT Saving! }
*/
setLastUpdate(time) { ///////// Public Interface ///////////
this.currentData.lastUpdate = time;
} /**
* Updates the last update field so we can send the savegame to the server,
/** * WITHOUT Saving!
* */
* @param {GameRoot} root setLastUpdate(time) {
*/ this.currentData.lastUpdate = time;
updateData(root) { }
// Construct a new serializer
const serializer = new SavegameSerializer(); /**
*
// let timer = performance.now(); * @param {GameRoot} root
const dump = serializer.generateDumpFromGameRoot(root); */
if (!dump) { updateData(root) {
return false; // Construct a new serializer
} const serializer = new SavegameSerializer();
const shadowData = Object.assign({}, this.currentData); // let timer = performance.now();
shadowData.dump = dump; const dump = serializer.generateDumpFromGameRoot(root);
shadowData.lastUpdate = new Date().getTime(); if (!dump) {
shadowData.version = this.getCurrentVersion(); return false;
}
const reader = this.getDumpReaderForExternalData(shadowData);
const shadowData = Object.assign({}, this.currentData);
// Validate (not in prod though) shadowData.dump = dump;
if (!G_IS_RELEASE) { shadowData.lastUpdate = new Date().getTime();
const validationResult = reader.validate(); shadowData.version = this.getCurrentVersion();
if (!validationResult) {
return false; const reader = this.getDumpReaderForExternalData(shadowData);
}
} // Validate (not in prod though)
if (!G_IS_RELEASE) {
// Save data const validationResult = reader.validate();
this.currentData = shadowData; if (!validationResult) {
} return false;
}
/** }
* Writes the savegame as well as its metadata
*/ // Save data
writeSavegameAndMetadata() { this.currentData = shadowData;
return this.writeAsync().then(() => this.saveMetadata()); }
}
/**
/** * Writes the savegame as well as its metadata
* Updates the savegames metadata */
*/ writeSavegameAndMetadata() {
saveMetadata() { return this.writeAsync().then(() => this.saveMetadata());
this.metaDataRef.lastUpdate = new Date().getTime(); }
this.metaDataRef.version = this.getCurrentVersion();
if (!this.hasGameDump()) { /**
this.metaDataRef.level = 0; * Updates the savegames metadata
} else { */
this.metaDataRef.level = this.currentData.dump.hubGoals.level; saveMetadata() {
} this.metaDataRef.lastUpdate = new Date().getTime();
this.metaDataRef.version = this.getCurrentVersion();
return this.app.savegameMgr.writeAsync(); if (!this.hasGameDump()) {
} this.metaDataRef.level = 0;
} else {
/** this.metaDataRef.level = this.currentData.dump.hubGoals.level;
* @see ReadWriteProxy.writeAsync }
* @returns {Promise<any>}
*/ return this.app.savegameMgr.writeAsync();
writeAsync() { }
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
return Promise.resolve(); /**
} * @see ReadWriteProxy.writeAsync
return super.writeAsync(); * @returns {Promise<any>}
} */
} writeAsync() {
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
return Promise.resolve();
}
return super.writeAsync();
}
}

View File

@ -1,45 +1,47 @@
import { BaseSavegameInterface } from "./savegame_interface"; import { BaseSavegameInterface } from "./savegame_interface";
import { SavegameInterface_V1000 } from "./schemas/1000"; import { SavegameInterface_V1000 } from "./schemas/1000";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { SavegameInterface_V1001 } from "./schemas/1001"; import { SavegameInterface_V1001 } from "./schemas/1001";
import { SavegameInterface_V1002 } from "./schemas/1002"; import { SavegameInterface_V1002 } from "./schemas/1002";
import { SavegameInterface_V1003 } from "./schemas/1003"; import { SavegameInterface_V1003 } from "./schemas/1003";
import { SavegameInterface_V1004 } from "./schemas/1004"; import { SavegameInterface_V1004 } from "./schemas/1004";
import { SavegameInterface_V1005 } from "./schemas/1005"; import { SavegameInterface_V1005 } from "./schemas/1005";
import { SavegameInterface_V1006 } from "./schemas/1006";
/** @type {Object.<number, typeof BaseSavegameInterface>} */
export const savegameInterfaces = { /** @type {Object.<number, typeof BaseSavegameInterface>} */
1000: SavegameInterface_V1000, export const savegameInterfaces = {
1001: SavegameInterface_V1001, 1000: SavegameInterface_V1000,
1002: SavegameInterface_V1002, 1001: SavegameInterface_V1001,
1003: SavegameInterface_V1003, 1002: SavegameInterface_V1002,
1004: SavegameInterface_V1004, 1003: SavegameInterface_V1003,
1005: SavegameInterface_V1005, 1004: SavegameInterface_V1004,
}; 1005: SavegameInterface_V1005,
1006: SavegameInterface_V1006,
const logger = createLogger("savegame_interface_registry"); };
/** const logger = createLogger("savegame_interface_registry");
* Returns if the given savegame has any supported interface
* @param {any} savegame /**
* @returns {BaseSavegameInterface|null} * Returns if the given savegame has any supported interface
*/ * @param {any} savegame
export function getSavegameInterface(savegame) { * @returns {BaseSavegameInterface|null}
if (!savegame || !savegame.version) { */
logger.warn("Savegame does not contain a valid version (undefined)"); export function getSavegameInterface(savegame) {
return null; if (!savegame || !savegame.version) {
} logger.warn("Savegame does not contain a valid version (undefined)");
const version = savegame.version; return null;
if (!Number.isInteger(version)) { }
logger.warn("Savegame does not contain a valid version (non-integer):", version); const version = savegame.version;
return null; if (!Number.isInteger(version)) {
} logger.warn("Savegame does not contain a valid version (non-integer):", version);
return null;
const interfaceClass = savegameInterfaces[version]; }
if (!interfaceClass) {
logger.warn("Version", version, "has no implemented interface!"); const interfaceClass = savegameInterfaces[version];
return null; if (!interfaceClass) {
} logger.warn("Version", version, "has no implemented interface!");
return null;
return new interfaceClass(savegame); }
}
return new interfaceClass(savegame);
}

View File

@ -0,0 +1,265 @@
import { gMetaBuildingRegistry } from "../../core/global_registries.js";
import { createLogger } from "../../core/logging.js";
import { MetaBeltBuilding } from "../../game/buildings/belt.js";
import { enumCutterVariants, MetaCutterBuilding } from "../../game/buildings/cutter.js";
import { MetaHubBuilding } from "../../game/buildings/hub.js";
import { enumMinerVariants, MetaMinerBuilding } from "../../game/buildings/miner.js";
import { MetaMixerBuilding } from "../../game/buildings/mixer.js";
import { enumPainterVariants, MetaPainterBuilding } from "../../game/buildings/painter.js";
import { enumRotaterVariants, MetaRotaterBuilding } from "../../game/buildings/rotater.js";
import { enumSplitterVariants, MetaSplitterBuilding } from "../../game/buildings/splitter.js";
import { MetaStackerBuilding } from "../../game/buildings/stacker.js";
import { enumTrashVariants, MetaTrashBuilding } from "../../game/buildings/trash.js";
import {
enumUndergroundBeltVariants,
MetaUndergroundBeltBuilding,
} from "../../game/buildings/underground_belt.js";
import { getCodeFromBuildingData } from "../../game/building_codes.js";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
import { Entity } from "../../game/entity.js";
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
import { SavegameInterface_V1005 } from "./1005.js";
const schema = require("./1006.json");
const logger = createLogger("savegame_interface/1006");
/**
*
* @param {typeof MetaBuilding} metaBuilding
* @param {string=} variant
* @param {number=} rotationVariant
*/
function findCode(metaBuilding, variant = defaultBuildingVariant, rotationVariant = 0) {
return getCodeFromBuildingData(gMetaBuildingRegistry.findByClass(metaBuilding), variant, rotationVariant);
}
export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
getVersion() {
return 1006;
}
getSchemaUncached() {
return schema;
}
static computeSpriteMapping() {
return {
// Belt
"sprites/blueprints/belt_top.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 0),
"sprites/blueprints/belt_left.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 1),
"sprites/blueprints/belt_right.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 2),
// Splitter
"sprites/blueprints/splitter.png": findCode(MetaSplitterBuilding),
"sprites/blueprints/splitter-compact.png": findCode(
MetaSplitterBuilding,
enumSplitterVariants.compact
),
"sprites/blueprints/splitter-compact-inverse.png": findCode(
MetaSplitterBuilding,
enumSplitterVariants.compactInverse
),
// Underground belt
"sprites/blueprints/underground_belt_entry.png": findCode(
MetaUndergroundBeltBuilding,
defaultBuildingVariant,
0
),
"sprites/blueprints/underground_belt_exit.png": findCode(
MetaUndergroundBeltBuilding,
defaultBuildingVariant,
1
),
"sprites/blueprints/underground_belt_entry-tier2.png": findCode(
MetaUndergroundBeltBuilding,
enumUndergroundBeltVariants.tier2,
0
),
"sprites/blueprints/underground_belt_exit-tier2.png": findCode(
MetaUndergroundBeltBuilding,
enumUndergroundBeltVariants.tier2,
1
),
// Miner
"sprites/blueprints/miner.png": findCode(MetaMinerBuilding),
"sprites/blueprints/miner-chainable.png": findCode(
MetaMinerBuilding,
enumMinerVariants.chainable,
0
),
// Cutter
"sprites/blueprints/cutter.png": findCode(MetaCutterBuilding),
"sprites/blueprints/cutter-quad.png": findCode(MetaCutterBuilding, enumCutterVariants.quad),
// Rotater
"sprites/blueprints/rotater.png": findCode(MetaRotaterBuilding),
"sprites/blueprints/rotater-ccw.png": findCode(MetaRotaterBuilding, enumRotaterVariants.ccw),
// Stacker
"sprites/blueprints/stacker.png": findCode(MetaStackerBuilding),
// Mixer
"sprites/blueprints/mixer.png": findCode(MetaMixerBuilding),
// Painter
"sprites/blueprints/painter.png": findCode(MetaPainterBuilding),
"sprites/blueprints/painter-mirrored.png": findCode(
MetaPainterBuilding,
enumPainterVariants.mirrored
),
"sprites/blueprints/painter-double.png": findCode(
MetaPainterBuilding,
enumPainterVariants.double
),
"sprites/blueprints/painter-quad.png": findCode(MetaPainterBuilding, enumPainterVariants.quad),
// Trash / Storage
"sprites/blueprints/trash.png": findCode(MetaTrashBuilding),
"sprites/blueprints/trash-storage.png": findCode(MetaTrashBuilding, enumTrashVariants.storage),
};
}
/**
* @param {import("../savegame_typedefs.js").SavegameData} data
*/
static migrate1005to1006(data) {
logger.log("Migrating 1005 to 1006");
const dump = data.dump;
if (!dump) {
return true;
}
// Update entities
const entities = dump.entities;
for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];
const components = entity.components;
this.migrateStaticComp1005to1006(entity);
// HUB
if (components.Hub) {
// @ts-ignore
components.Hub = {};
}
// Item Processor
if (components.ItemProcessor) {
// @ts-ignore
components.ItemProcessor = {
nextOutputSlot: 0,
};
}
// OLD: Unremovable component
// @ts-ignore
if (components.Unremovable) {
// @ts-ignore
delete components.Unremovable;
}
// OLD: ReplaceableMapEntity
// @ts-ignore
if (components.ReplaceableMapEntity) {
// @ts-ignore
delete components.ReplaceableMapEntity;
}
// ItemAcceptor
if (components.ItemAcceptor) {
// @ts-ignore
components.ItemAcceptor = {};
}
// Belt
if (components.Belt) {
// @ts-ignore
components.Belt = {};
}
// Item Ejector
if (components.ItemEjector) {
// @ts-ignore
components.ItemEjector = {
slots: [],
};
}
// UndergroundBelt
if (components.UndergroundBelt) {
// @ts-ignore
components.UndergroundBelt = {
pendingItems: [],
};
}
// Miner
if (components.Miner) {
// @ts-ignore
delete components.Miner.chainable;
components.Miner.lastMiningTime = 0;
components.Miner.itemChainBuffer = [];
}
// Storage
if (components.Storage) {
// @ts-ignore
components.Storage = {
storedCount: 0,
storedItem: null,
};
}
}
}
/**
*
* @param {Entity} entity
*/
static migrateStaticComp1005to1006(entity) {
const spriteMapping = this.computeSpriteMapping();
const staticComp = entity.components.StaticMapEntity;
/** @type {StaticMapEntityComponent} */
const newStaticComp = {};
newStaticComp.origin = staticComp.origin;
newStaticComp.originalRotation = staticComp.originalRotation;
newStaticComp.rotation = staticComp.rotation;
// @ts-ignore
newStaticComp.code = spriteMapping[staticComp.blueprintSpriteKey];
// Hub special case
if (entity.components.Hub) {
newStaticComp.code = findCode(MetaHubBuilding);
}
// Belt special case
if (entity.components.Belt) {
const actualCode = {
top: findCode(MetaBeltBuilding, defaultBuildingVariant, 0),
left: findCode(MetaBeltBuilding, defaultBuildingVariant, 1),
right: findCode(MetaBeltBuilding, defaultBuildingVariant, 2),
}[entity.components.Belt.direction];
if (actualCode !== newStaticComp.code) {
if (G_IS_DEV) {
console.warn("Belt mismatch");
}
newStaticComp.code = actualCode;
}
}
if (!newStaticComp.code) {
throw new Error(
// @ts-ignore
"1006 Migration: Could not reconstruct code for " + staticComp.blueprintSpriteKey
);
}
entity.components.StaticMapEntity = newStaticComp;
}
}

View File

@ -0,0 +1,5 @@
{
"type": "object",
"required": [],
"additionalProperties": true
}

View File

@ -1,3 +1,4 @@
import { globalConfig } from "../core/config";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { getBuildingDataFromCode } from "../game/building_codes"; import { getBuildingDataFromCode } from "../game/building_codes";
@ -78,7 +79,9 @@ export class SerializerInternal {
deserializeComponents(root, entity, data) { deserializeComponents(root, entity, data) {
for (const componentId in data) { for (const componentId in data) {
if (!entity.components[componentId]) { if (!entity.components[componentId]) {
logger.warn("Entity no longer has component:", componentId); if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) {
logger.warn("Entity no longer has component:", componentId);
}
continue; continue;
} }