From bf7e1887a80764413c2e5ad89542ba223c30914c Mon Sep 17 00:00:00 2001 From: tobspr Date: Tue, 1 Feb 2022 14:37:53 +0100 Subject: [PATCH] Add example for storing data in the savegame --- mod_examples/README.md | 1 + mod_examples/storing_data_in_savegame.js | 78 ++++++++++++++++++++++++ src/js/mods/mod_signals.js | 3 + src/js/savegame/savegame.js | 1 + src/js/savegame/savegame_serializer.js | 12 +++- src/js/savegame/savegame_typedefs.js | 3 +- src/js/savegame/schemas/1010.js | 4 ++ 7 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 mod_examples/storing_data_in_savegame.js diff --git a/mod_examples/README.md b/mod_examples/README.md index 6ea04c38..579751f9 100644 --- a/mod_examples/README.md +++ b/mod_examples/README.md @@ -38,6 +38,7 @@ To get into shapez.io modding, I highly recommend checking out all of the exampl | [modify_theme.js](modify_theme.js) | Modifies the default game themes | Modifying the builtin themes | | [custom_theme.js](custom_theme.js) | Adds a new UI and map theme | Adding a new game theme | | [mod_settings.js](mod_settings.js) | Shows a dialog counting how often the mod has been launched | Reading and storing mod settings | +| [storing_data_in_savegame.js](storing_data_in_savegame.js) | Shows how to store custom (structured) data in the savegame | Storing custom data in savegame | | [modify_existing_building.js](modify_existing_building.js) | Makes the rotator building always unlocked and adds a new statistic to the building panel | Modifying a builtin building, replacing builtin methods | | [modify_ui.js](modify_ui.js) | Shows how to add custom IU elements to builtin game states (the Main Menu in this case) | Extending builtin UI states, Adding CSS | | [pasting.js](pasting.js) | Shows a dialog when pasting text in the game | Listening to paste events | diff --git a/mod_examples/storing_data_in_savegame.js b/mod_examples/storing_data_in_savegame.js new file mode 100644 index 00000000..92f7733b --- /dev/null +++ b/mod_examples/storing_data_in_savegame.js @@ -0,0 +1,78 @@ +// @ts-nocheck +const METADATA = { + website: "https://tobspr.io", + author: "tobspr", + name: "Mod Example: Storing Data in Savegame", + version: "1", + id: "storing-savegame-data", + description: "Shows how to add custom data to a savegame", + minimumGameVersion: ">=1.5.0", +}; + +class Mod extends shapez.Mod { + init() { + //////////////////////////////////////////////////////////////////// + // Option 1: For simple data + this.signals.gameSerialized.add((root, data) => { + data.modExtraData["storing-savegame-data"] = Math.random(); + }); + + this.signals.gameDeserialized.add((root, data) => { + alert("The value stored in the savegame was: " + data.modExtraData["storing-savegame-data"]); + }); + + //////////////////////////////////////////////////////////////////// + // Option 2: If you need a structured way of storing data + + class SomeSerializableObject extends shapez.BasicSerializableObject { + static getId() { + return "SomeSerializableObject"; + } + + static getSchema() { + return { + someInt: shapez.types.int, + someString: shapez.types.string, + someVector: shapez.types.vector, + + // this value is allowed to be null + nullableInt: shapez.types.nullable(shapez.types.int), + + // There is a lot more .. be sure to checkout src/js/savegame/serialization.js + // You can have maps, classes, arrays etc.. + // And if you need something specific you can always ask in the modding discord. + }; + } + + constructor() { + super(); + this.someInt = 42; + this.someString = "Hello World"; + this.someVector = new shapez.Vector(1, 2); + + this.nullableInt = null; + } + } + + // Store our object in the global game root + this.signals.gameInitialized.add(root => { + root.myObject = new SomeSerializableObject(); + }); + + // Save it within the savegame + this.signals.gameSerialized.add((root, data) => { + data.modExtraData["storing-savegame-data-2"] = root.myObject.serialize(); + }); + + // Restore it when the savegame is loaded + this.signals.gameDeserialized.add((root, data) => { + const errorText = root.myObject.deserialize(data.modExtraData["storing-savegame-data-2"]); + if (errorText) { + alert("Mod failed to deserialize from savegame: " + errorText); + } + alert("The other value stored in the savegame (option 2) was " + root.myObject.someInt); + }); + + //////////////////////////////////////////////////////////////////// + } +} diff --git a/src/js/mods/mod_signals.js b/src/js/mods/mod_signals.js index c311af29..a534dd89 100644 --- a/src/js/mods/mod_signals.js +++ b/src/js/mods/mod_signals.js @@ -27,4 +27,7 @@ export const MOD_SIGNALS = { gameStarted: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()), stateEntered: /** @type {TypedSignal<[GameState]>} */ (new Signal()), + + gameSerialized: /** @type {TypedSignal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ (new Signal()), + gameDeserialized: /** @type {TypedSignal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ (new Signal()), }; diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index a2f27d0f..b4472b2b 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -162,6 +162,7 @@ export class Savegame extends ReadWriteProxy { SavegameInterface_V1009.migrate1008to1009(data); data.version = 1009; } + if (data.version === 1009) { SavegameInterface_V1010.migrate1009to1010(data); data.version = 1010; diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index 3230cdd5..f95c9896 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -1,9 +1,8 @@ import { ExplainedResult } from "../core/explained_result"; -import { createLogger } from "../core/logging"; import { gComponentRegistry } from "../core/global_registries"; +import { createLogger } from "../core/logging"; +import { MOD_SIGNALS } from "../mods/mod_signals"; import { SerializerInternal } from "./serializer_internal"; -import { HUDPinnedShapes } from "../game/hud/parts/pinned_shapes"; -import { HUDWaypoints } from "../game/hud/parts/waypoints"; /** * @typedef {import("../game/component").Component} Component @@ -42,8 +41,12 @@ export class SavegameSerializer { beltPaths: root.systemMgr.systems.belt.serializePaths(), pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null, waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null, + + modExtraData: {}, }; + MOD_SIGNALS.gameSerialized.dispatch(root, data); + if (G_IS_DEV) { if (sanityChecks) { // Sanity check @@ -151,6 +154,9 @@ export class SavegameSerializer { return ExplainedResult.bad(errorReason); } + // Mods + MOD_SIGNALS.gameDeserialized.dispatch(root, savegame); + return ExplainedResult.good(); } } diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index c72d83e6..b1980115 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -25,7 +25,8 @@ * pinnedShapes: any, * waypoints: any, * entities: Array, - * beltPaths: Array + * beltPaths: Array, + * modExtraData: Object * }} SerializedGame * * @typedef {{ diff --git a/src/js/savegame/schemas/1010.js b/src/js/savegame/schemas/1010.js index 5453f3e2..8f480800 100644 --- a/src/js/savegame/schemas/1010.js +++ b/src/js/savegame/schemas/1010.js @@ -20,5 +20,9 @@ export class SavegameInterface_V1010 extends SavegameInterface_V1009 { logger.log("Migrating 1009 to 1010"); data.mods = []; + + if (data.dump) { + data.dump.modExtraData = {}; + } } }