From 091401e52b26e75e82da72aeb7d77e5ebce6b482 Mon Sep 17 00:00:00 2001 From: tobspr Date: Sat, 29 Aug 2020 23:38:49 +0200 Subject: [PATCH] Store wires state on save --- src/js/game/automatic_save.js | 161 +++++++++++----------- src/js/game/game_system_manager.js | 6 +- src/js/game/systems/wire.js | 32 +++-- src/js/savegame/serializer_internal.js | 182 ++++++++++++------------- 4 files changed, 192 insertions(+), 189 deletions(-) diff --git a/src/js/game/automatic_save.js b/src/js/game/automatic_save.js index e32b9b62..9d841966 100644 --- a/src/js/game/automatic_save.js +++ b/src/js/game/automatic_save.js @@ -1,82 +1,79 @@ -import { GameRoot } from "./root"; -import { globalConfig, IS_DEBUG } from "../core/config"; -import { createLogger } from "../core/logging"; - -// How important it is that a savegame is created -/** - * @enum {number} - */ -export const enumSavePriority = { - regular: 2, - asap: 100, -}; - -const logger = createLogger("autosave"); - -// Internals -let MIN_INTERVAL_SECS = 60; - -export class AutomaticSave { - constructor(root) { - /** @type {GameRoot} */ - this.root = root; - - // Store the current maximum save importance - this.saveImportance = enumSavePriority.regular; - - this.lastSaveAttempt = -1000; - } - - setSaveImportance(importance) { - this.saveImportance = Math.max(this.saveImportance, importance); - } - - doSave() { - if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { - return; - } - - this.root.gameState.doSave(); - this.saveImportance = enumSavePriority.regular; - } - - update() { - if (!this.root.gameInitialized) { - // Bad idea - return; - } - - const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds(); - if (!saveInterval) { - // Disabled - return; - } - - // Check when the last save was, but make sure that if it fails, we don't spam - const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); - - const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; - let shouldSave = false; - - switch (this.saveImportance) { - case enumSavePriority.asap: - // High always should save - shouldSave = true; - break; - - case enumSavePriority.regular: - // Could determine if there is a good / bad point here - shouldSave = secondsSinceLastSave > saveInterval; - break; - - default: - assert(false, "Unknown save prio: " + this.saveImportance); - break; - } - if (shouldSave) { - logger.log("Saving automatically"); - this.lastSaveAttempt = Date.now(); - this.doSave(); - } - } -} +import { globalConfig } from "../core/config"; +import { createLogger } from "../core/logging"; +import { GameRoot } from "./root"; + +// How important it is that a savegame is created +/** + * @enum {number} + */ +export const enumSavePriority = { + regular: 2, + asap: 100, +}; + +const logger = createLogger("autosave"); + +export class AutomaticSave { + constructor(root) { + /** @type {GameRoot} */ + this.root = root; + + // Store the current maximum save importance + this.saveImportance = enumSavePriority.regular; + + this.lastSaveAttempt = -1000; + } + + setSaveImportance(importance) { + this.saveImportance = Math.max(this.saveImportance, importance); + } + + doSave() { + if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { + return; + } + + this.root.gameState.doSave(); + this.saveImportance = enumSavePriority.regular; + } + + update() { + if (!this.root.gameInitialized) { + // Bad idea + return; + } + + const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds(); + if (!saveInterval) { + // Disabled + return; + } + + // Check when the last save was, but make sure that if it fails, we don't spam + const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); + + const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; + let shouldSave = false; + + switch (this.saveImportance) { + case enumSavePriority.asap: + // High always should save + shouldSave = true; + break; + + case enumSavePriority.regular: + // Could determine if there is a good / bad point here + shouldSave = secondsSinceLastSave > saveInterval; + break; + + default: + assert(false, "Unknown save prio: " + this.saveImportance); + break; + } + if (shouldSave) { + logger.log("Saving automatically"); + this.lastSaveAttempt = Date.now(); + this.doSave(); + } + } +} diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 71c3fe94..bcbb7ccf 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -142,14 +142,14 @@ export class GameSystemManager { // WIRES section add("lever", LeverSystem); + // Wires must be before all gate, signal etc logic! + add("wire", WireSystem); + // IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates, // processors etc. In phase 2 we propagate it through the wires network add("logicGate", LogicGateSystem); add("beltReader", BeltReaderSystem); - // Wires must be after all gate, signal etc logic! - add("wire", WireSystem); - add("display", DisplaySystem); add("itemProcessorOverlays", ItemProcessorOverlaysSystem); diff --git a/src/js/game/systems/wire.js b/src/js/game/systems/wire.js index 853fc725..fa9287e1 100644 --- a/src/js/game/systems/wire.js +++ b/src/js/game/systems/wire.js @@ -120,6 +120,7 @@ export class WireSystem extends GameSystemWithFilter { this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this); this.needsRecompute = true; + this.isFirstRecompute = true; this.staleArea = new StaleAreaDetector({ root: this.root, @@ -157,23 +158,28 @@ export class WireSystem extends GameSystemWithFilter { this.networks = []; - // Clear all network references const wireEntities = this.root.entityMgr.getAllWithComponent(WireComponent); - for (let i = 0; i < wireEntities.length; ++i) { - wireEntities[i].components.Wire.linkedNetwork = null; - } - const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent); - for (let i = 0; i < tunnelEntities.length; ++i) { - tunnelEntities[i].components.WireTunnel.linkedNetworks = []; - } - const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent); - for (let i = 0; i < pinEntities.length; ++i) { - const slots = pinEntities[i].components.WiredPins.slots; - for (let k = 0; k < slots.length; ++k) { - slots[k].linkedNetwork = null; + + // Clear all network references, but not on the first update since thats the deserializing one + if (!this.isFirstRecompute) { + for (let i = 0; i < wireEntities.length; ++i) { + wireEntities[i].components.Wire.linkedNetwork = null; } + for (let i = 0; i < tunnelEntities.length; ++i) { + tunnelEntities[i].components.WireTunnel.linkedNetworks = []; + } + + for (let i = 0; i < pinEntities.length; ++i) { + const slots = pinEntities[i].components.WiredPins.slots; + for (let k = 0; k < slots.length; ++k) { + slots[k].linkedNetwork = null; + } + } + } else { + logger.log("Recomputing wires first time"); + this.isFirstRecompute = false; } VERBOSE_WIRES && logger.log("Recomputing slots"); diff --git a/src/js/savegame/serializer_internal.js b/src/js/savegame/serializer_internal.js index ee32dff6..6e5dfbc2 100644 --- a/src/js/savegame/serializer_internal.js +++ b/src/js/savegame/serializer_internal.js @@ -1,91 +1,91 @@ -import { createLogger } from "../core/logging"; -import { Vector } from "../core/vector"; -import { getBuildingDataFromCode } from "../game/building_codes"; -import { Entity } from "../game/entity"; -import { GameRoot } from "../game/root"; - -const logger = createLogger("serializer_internal"); - -// Internal serializer methods -export class SerializerInternal { - /** - * Serializes an array of entities - * @param {Array} array - */ - serializeEntityArray(array) { - const serialized = []; - for (let i = 0; i < array.length; ++i) { - const entity = array[i]; - if (!entity.queuedForDestroy && !entity.destroyed) { - serialized.push(entity.serialize()); - } - } - return serialized; - } - - /** - * - * @param {GameRoot} root - * @param {Array} array - * @returns {string|void} - */ - deserializeEntityArray(root, array) { - for (let i = 0; i < array.length; ++i) { - this.deserializeEntity(root, array[i]); - } - } - - /** - * - * @param {GameRoot} root - * @param {Entity} payload - */ - deserializeEntity(root, payload) { - const staticData = payload.components.StaticMapEntity; - assert(staticData, "entity has no static data"); - - const code = staticData.code; - const data = getBuildingDataFromCode(code); - - const metaBuilding = data.metaInstance; - - const entity = metaBuilding.createEntity({ - root, - origin: Vector.fromSerializedObject(staticData.origin), - rotation: staticData.rotation, - originalRotation: staticData.originalRotation, - rotationVariant: data.rotationVariant, - variant: data.variant, - }); - - entity.uid = payload.uid; - - this.deserializeComponents(root, entity, payload.components); - - root.entityMgr.registerEntity(entity, payload.uid); - root.map.placeStaticEntity(entity); - } - - /////// COMPONENTS //// - - /** - * Deserializes components of an entity - * @param {GameRoot} root - * @param {Entity} entity - * @param {Object.} data - * @returns {string|void} - */ - deserializeComponents(root, entity, data) { - for (const componentId in data) { - if (!entity.components[componentId]) { - logger.warn("Entity no longer has component:", componentId); - continue; - } - - const errorStatus = entity.components[componentId].deserialize(data[componentId], root); - if (errorStatus) { - return errorStatus; - } - } - } -} +import { createLogger } from "../core/logging"; +import { Vector } from "../core/vector"; +import { getBuildingDataFromCode } from "../game/building_codes"; +import { Entity } from "../game/entity"; +import { GameRoot } from "../game/root"; + +const logger = createLogger("serializer_internal"); + +// Internal serializer methods +export class SerializerInternal { + /** + * Serializes an array of entities + * @param {Array} array + */ + serializeEntityArray(array) { + const serialized = []; + for (let i = 0; i < array.length; ++i) { + const entity = array[i]; + if (!entity.queuedForDestroy && !entity.destroyed) { + serialized.push(entity.serialize()); + } + } + return serialized; + } + + /** + * + * @param {GameRoot} root + * @param {Array} array + * @returns {string|void} + */ + deserializeEntityArray(root, array) { + for (let i = 0; i < array.length; ++i) { + this.deserializeEntity(root, array[i]); + } + } + + /** + * + * @param {GameRoot} root + * @param {Entity} payload + */ + deserializeEntity(root, payload) { + const staticData = payload.components.StaticMapEntity; + assert(staticData, "entity has no static data"); + + const code = staticData.code; + const data = getBuildingDataFromCode(code); + + const metaBuilding = data.metaInstance; + + const entity = metaBuilding.createEntity({ + root, + origin: Vector.fromSerializedObject(staticData.origin), + rotation: staticData.rotation, + originalRotation: staticData.originalRotation, + rotationVariant: data.rotationVariant, + variant: data.variant, + }); + + entity.uid = payload.uid; + + this.deserializeComponents(root, entity, payload.components); + + root.entityMgr.registerEntity(entity, payload.uid); + root.map.placeStaticEntity(entity); + } + + /////// COMPONENTS //// + + /** + * Deserializes components of an entity + * @param {GameRoot} root + * @param {Entity} entity + * @param {Object.} data + * @returns {string|void} + */ + deserializeComponents(root, entity, data) { + for (const componentId in data) { + if (!entity.components[componentId]) { + logger.warn("Entity no longer has component:", componentId); + continue; + } + + const errorStatus = entity.components[componentId].deserialize(data[componentId], root); + if (errorStatus) { + return errorStatus; + } + } + } +}