1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-07 10:03:59 +00:00

Store wires state on save

This commit is contained in:
tobspr 2020-08-29 23:38:49 +02:00
parent b478f4be63
commit 091401e52b
4 changed files with 192 additions and 189 deletions

View File

@ -1,82 +1,79 @@
import { GameRoot } from "./root"; import { globalConfig } from "../core/config";
import { globalConfig, IS_DEBUG } from "../core/config"; import { createLogger } from "../core/logging";
import { createLogger } from "../core/logging"; import { GameRoot } from "./root";
// How important it is that a savegame is created // How important it is that a savegame is created
/** /**
* @enum {number} * @enum {number}
*/ */
export const enumSavePriority = { export const enumSavePriority = {
regular: 2, regular: 2,
asap: 100, asap: 100,
}; };
const logger = createLogger("autosave"); const logger = createLogger("autosave");
// Internals export class AutomaticSave {
let MIN_INTERVAL_SECS = 60; constructor(root) {
/** @type {GameRoot} */
export class AutomaticSave { this.root = root;
constructor(root) {
/** @type {GameRoot} */ // Store the current maximum save importance
this.root = root; this.saveImportance = enumSavePriority.regular;
// Store the current maximum save importance this.lastSaveAttempt = -1000;
this.saveImportance = enumSavePriority.regular; }
this.lastSaveAttempt = -1000; setSaveImportance(importance) {
} this.saveImportance = Math.max(this.saveImportance, importance);
}
setSaveImportance(importance) {
this.saveImportance = Math.max(this.saveImportance, importance); doSave() {
} if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
return;
doSave() { }
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
return; this.root.gameState.doSave();
} this.saveImportance = enumSavePriority.regular;
}
this.root.gameState.doSave();
this.saveImportance = enumSavePriority.regular; update() {
} if (!this.root.gameInitialized) {
// Bad idea
update() { return;
if (!this.root.gameInitialized) { }
// Bad idea
return; const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds();
} if (!saveInterval) {
// Disabled
const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds(); return;
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());
// Check when the last save was, but make sure that if it fails, we don't spam const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0;
const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); let shouldSave = false;
const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; switch (this.saveImportance) {
let shouldSave = false; case enumSavePriority.asap:
// High always should save
switch (this.saveImportance) { shouldSave = true;
case enumSavePriority.asap: break;
// High always should save
shouldSave = true; case enumSavePriority.regular:
break; // Could determine if there is a good / bad point here
shouldSave = secondsSinceLastSave > saveInterval;
case enumSavePriority.regular: break;
// Could determine if there is a good / bad point here
shouldSave = secondsSinceLastSave > saveInterval; default:
break; assert(false, "Unknown save prio: " + this.saveImportance);
break;
default: }
assert(false, "Unknown save prio: " + this.saveImportance); if (shouldSave) {
break; logger.log("Saving automatically");
} this.lastSaveAttempt = Date.now();
if (shouldSave) { this.doSave();
logger.log("Saving automatically"); }
this.lastSaveAttempt = Date.now(); }
this.doSave(); }
}
}
}

View File

@ -142,14 +142,14 @@ export class GameSystemManager {
// WIRES section // WIRES section
add("lever", LeverSystem); 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, // 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 // processors etc. In phase 2 we propagate it through the wires network
add("logicGate", LogicGateSystem); add("logicGate", LogicGateSystem);
add("beltReader", BeltReaderSystem); add("beltReader", BeltReaderSystem);
// Wires must be after all gate, signal etc logic!
add("wire", WireSystem);
add("display", DisplaySystem); add("display", DisplaySystem);
add("itemProcessorOverlays", ItemProcessorOverlaysSystem); add("itemProcessorOverlays", ItemProcessorOverlaysSystem);

View File

@ -120,6 +120,7 @@ export class WireSystem extends GameSystemWithFilter {
this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this); this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this);
this.needsRecompute = true; this.needsRecompute = true;
this.isFirstRecompute = true;
this.staleArea = new StaleAreaDetector({ this.staleArea = new StaleAreaDetector({
root: this.root, root: this.root,
@ -157,23 +158,28 @@ export class WireSystem extends GameSystemWithFilter {
this.networks = []; this.networks = [];
// Clear all network references
const wireEntities = this.root.entityMgr.getAllWithComponent(WireComponent); 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); 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); const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent);
for (let i = 0; i < pinEntities.length; ++i) {
const slots = pinEntities[i].components.WiredPins.slots; // Clear all network references, but not on the first update since thats the deserializing one
for (let k = 0; k < slots.length; ++k) { if (!this.isFirstRecompute) {
slots[k].linkedNetwork = null; 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"); VERBOSE_WIRES && logger.log("Recomputing slots");

View File

@ -1,91 +1,91 @@
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";
import { Entity } from "../game/entity"; import { Entity } from "../game/entity";
import { GameRoot } from "../game/root"; import { GameRoot } from "../game/root";
const logger = createLogger("serializer_internal"); const logger = createLogger("serializer_internal");
// Internal serializer methods // Internal serializer methods
export class SerializerInternal { export class SerializerInternal {
/** /**
* Serializes an array of entities * Serializes an array of entities
* @param {Array<Entity>} array * @param {Array<Entity>} array
*/ */
serializeEntityArray(array) { serializeEntityArray(array) {
const serialized = []; const serialized = [];
for (let i = 0; i < array.length; ++i) { for (let i = 0; i < array.length; ++i) {
const entity = array[i]; const entity = array[i];
if (!entity.queuedForDestroy && !entity.destroyed) { if (!entity.queuedForDestroy && !entity.destroyed) {
serialized.push(entity.serialize()); serialized.push(entity.serialize());
} }
} }
return serialized; return serialized;
} }
/** /**
* *
* @param {GameRoot} root * @param {GameRoot} root
* @param {Array<any>} array * @param {Array<Entity>} array
* @returns {string|void} * @returns {string|void}
*/ */
deserializeEntityArray(root, array) { deserializeEntityArray(root, array) {
for (let i = 0; i < array.length; ++i) { for (let i = 0; i < array.length; ++i) {
this.deserializeEntity(root, array[i]); this.deserializeEntity(root, array[i]);
} }
} }
/** /**
* *
* @param {GameRoot} root * @param {GameRoot} root
* @param {Entity} payload * @param {Entity} payload
*/ */
deserializeEntity(root, payload) { deserializeEntity(root, payload) {
const staticData = payload.components.StaticMapEntity; const staticData = payload.components.StaticMapEntity;
assert(staticData, "entity has no static data"); assert(staticData, "entity has no static data");
const code = staticData.code; const code = staticData.code;
const data = getBuildingDataFromCode(code); const data = getBuildingDataFromCode(code);
const metaBuilding = data.metaInstance; const metaBuilding = data.metaInstance;
const entity = metaBuilding.createEntity({ const entity = metaBuilding.createEntity({
root, root,
origin: Vector.fromSerializedObject(staticData.origin), origin: Vector.fromSerializedObject(staticData.origin),
rotation: staticData.rotation, rotation: staticData.rotation,
originalRotation: staticData.originalRotation, originalRotation: staticData.originalRotation,
rotationVariant: data.rotationVariant, rotationVariant: data.rotationVariant,
variant: data.variant, variant: data.variant,
}); });
entity.uid = payload.uid; entity.uid = payload.uid;
this.deserializeComponents(root, entity, payload.components); this.deserializeComponents(root, entity, payload.components);
root.entityMgr.registerEntity(entity, payload.uid); root.entityMgr.registerEntity(entity, payload.uid);
root.map.placeStaticEntity(entity); root.map.placeStaticEntity(entity);
} }
/////// COMPONENTS //// /////// COMPONENTS ////
/** /**
* Deserializes components of an entity * Deserializes components of an entity
* @param {GameRoot} root * @param {GameRoot} root
* @param {Entity} entity * @param {Entity} entity
* @param {Object.<string, any>} data * @param {Object.<string, any>} data
* @returns {string|void} * @returns {string|void}
*/ */
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); logger.warn("Entity no longer has component:", componentId);
continue; continue;
} }
const errorStatus = entity.components[componentId].deserialize(data[componentId], root); const errorStatus = entity.components[componentId].deserialize(data[componentId], root);
if (errorStatus) { if (errorStatus) {
return errorStatus; return errorStatus;
} }
} }
} }
} }