Optimize performance by using singletons for items

This commit is contained in:
tobspr 2020-08-14 13:09:10 +02:00
parent 3c34227c24
commit 8c39d31c5b
18 changed files with 145 additions and 88 deletions

View File

@ -1,14 +1,14 @@
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { gItemRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Rectangle } from "../core/rectangle"; import { Rectangle } from "../core/rectangle";
import { epsilonCompare, round4Digits } from "../core/utils"; import { epsilonCompare, round4Digits } from "../core/utils";
import { enumDirection, enumDirectionToVector, Vector, enumInvertedDirections } from "../core/vector"; import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector";
import { BasicSerializableObject, types } from "../savegame/serialization"; import { BasicSerializableObject, types } from "../savegame/serialization";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { GameRoot, enumLayer } from "./root"; import { typeItemSingleton } from "./item_resolver";
import { enumLayer, GameRoot } from "./root";
const logger = createLogger("belt_path"); const logger = createLogger("belt_path");
@ -29,7 +29,7 @@ export class BeltPath extends BasicSerializableObject {
static getSchema() { static getSchema() {
return { return {
entityPath: types.array(types.entity), entityPath: types.array(types.entity),
items: types.array(types.pair(types.ufloat, types.obj(gItemRegistry))), items: types.array(types.pair(types.ufloat, typeItemSingleton)),
spacingToFirstItem: types.ufloat, spacingToFirstItem: types.ufloat,
}; };
} }

View File

@ -2,6 +2,7 @@ import { gItemRegistry } from "../../core/global_registries";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { Component } from "../component"; import { Component } from "../component";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { typeItemSingleton } from "../item_resolver";
export class ConstantSignalComponent extends Component { export class ConstantSignalComponent extends Component {
static getId() { static getId() {
@ -10,7 +11,7 @@ export class ConstantSignalComponent extends Component {
static getSchema() { static getSchema() {
return { return {
signal: types.nullable(types.obj(gItemRegistry)), signal: types.nullable(typeItemSingleton),
}; };
} }

View File

@ -1,11 +1,10 @@
import { Vector, enumDirection, enumDirectionToVector } from "../../core/vector"; import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
import { BaseItem } from "../base_item";
import { Component } from "../component";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { gItemRegistry } from "../../core/global_registries"; import { BaseItem } from "../base_item";
import { Entity } from "../entity";
import { enumLayer } from "../root";
import { BeltPath } from "../belt_path"; import { BeltPath } from "../belt_path";
import { Component } from "../component";
import { Entity } from "../entity";
import { typeItemSingleton } from "../item_resolver";
/** /**
* @typedef {{ * @typedef {{
@ -29,7 +28,7 @@ export class ItemEjectorComponent extends Component {
return { return {
slots: types.array( slots: types.array(
types.structured({ types.structured({
item: types.nullable(types.obj(gItemRegistry)), item: types.nullable(typeItemSingleton),
progress: types.float, progress: types.float,
}) })
), ),

View File

@ -1,7 +1,7 @@
import { gItemRegistry } from "../../core/global_registries";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { Component } from "../component"; import { Component } from "../component";
import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */ /** @enum {string} */
export const enumItemProcessorTypes = { export const enumItemProcessorTypes = {
@ -32,13 +32,13 @@ export class ItemProcessorComponent extends Component {
nextOutputSlot: types.uint, nextOutputSlot: types.uint,
inputSlots: types.array( inputSlots: types.array(
types.structured({ types.structured({
item: types.obj(gItemRegistry), item: typeItemSingleton,
sourceSlot: types.uint, sourceSlot: types.uint,
}) })
), ),
itemsToEject: types.array( itemsToEject: types.array(
types.structured({ types.structured({
item: types.obj(gItemRegistry), item: typeItemSingleton,
requiredSlot: types.nullable(types.uint), requiredSlot: types.nullable(types.uint),
preferredSlot: types.nullable(types.uint), preferredSlot: types.nullable(types.uint),
}) })

View File

@ -1,8 +1,7 @@
import { globalConfig } from "../../core/config";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { Component } from "../component";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { gItemRegistry } from "../../core/global_registries"; import { Component } from "../component";
import { typeItemSingleton } from "../item_resolver";
const chainBufferSize = 3; const chainBufferSize = 3;
@ -15,7 +14,7 @@ export class MinerComponent extends Component {
// cachedMinedItem is not serialized. // cachedMinedItem is not serialized.
return { return {
lastMiningTime: types.ufloat, lastMiningTime: types.ufloat,
itemChainBuffer: types.array(types.obj(gItemRegistry)), itemChainBuffer: types.array(typeItemSingleton),
}; };
} }

View File

@ -1,9 +1,7 @@
import { Component } from "../component";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { gItemRegistry } from "../../core/global_registries";
import { BaseItem, enumItemType } from "../base_item"; import { BaseItem, enumItemType } from "../base_item";
import { ColorItem } from "../items/color_item"; import { Component } from "../component";
import { ShapeItem } from "../items/shape_item"; import { typeItemSingleton } from "../item_resolver";
export class StorageComponent extends Component { export class StorageComponent extends Component {
static getId() { static getId() {
@ -13,7 +11,7 @@ export class StorageComponent extends Component {
static getSchema() { static getSchema() {
return { return {
storedCount: types.uint, storedCount: types.uint,
storedItem: types.nullable(types.obj(gItemRegistry)), storedItem: types.nullable(typeItemSingleton),
}; };
} }

View File

@ -1,10 +1,9 @@
import { BaseItem } from "../base_item";
import { Component } from "../component";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { types } from "../../savegame/serialization"; import { types } from "../../savegame/serialization";
import { gItemRegistry } from "../../core/global_registries"; import { BaseItem } from "../base_item";
import { Component } from "../component";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { enumLayer } from "../root"; import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */ /** @enum {string} */
export const enumUndergroundBeltMode = { export const enumUndergroundBeltMode = {
@ -26,7 +25,7 @@ export class UndergroundBeltComponent extends Component {
static getSchema() { static getSchema() {
return { return {
pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.float)), pendingItems: types.array(types.pair(typeItemSingleton, types.float)),
}; };
} }

View File

@ -13,7 +13,6 @@ import { randomInt, round2Digits } from "../core/utils";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { Savegame } from "../savegame/savegame"; import { Savegame } from "../savegame/savegame";
import { SavegameSerializer } from "../savegame/savegame_serializer"; import { SavegameSerializer } from "../savegame/savegame_serializer";
import { InGameState } from "../states/ingame";
import { AutomaticSave } from "./automatic_save"; import { AutomaticSave } from "./automatic_save";
import { MetaHubBuilding } from "./buildings/hub"; import { MetaHubBuilding } from "./buildings/hub";
import { Camera } from "./camera"; import { Camera } from "./camera";
@ -67,7 +66,7 @@ export class GameCore {
/** /**
* Initializes the root object which stores all game related data. The state * Initializes the root object which stores all game related data. The state
* is required as a back reference (used sometimes) * is required as a back reference (used sometimes)
* @param {InGameState} parentState * @param {import("../states/ingame").InGameState} parentState
* @param {Savegame} savegame * @param {Savegame} savegame
*/ */
initializeRoot(parentState, savegame) { initializeRoot(parentState, savegame) {

View File

@ -0,0 +1,33 @@
import { types } from "../savegame/serialization";
import { gItemRegistry } from "../core/global_registries";
import { BooleanItem, BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "./items/boolean_item";
import { ShapeItem } from "./items/shape_item";
import { ColorItem, COLOR_ITEM_SINGLETONS } from "./items/color_item";
/**
* Resolves items so we share instances
* @param {import("../savegame/savegame_serializer").GameRoot} root
* @param {{$: string, data: any }} data
*/
export function itemResolverSingleton(root, data) {
const itemType = data.$;
const itemData = data.data;
switch (itemType) {
case BooleanItem.getId(): {
return itemData ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
}
case ShapeItem.getId(): {
return root.shapeDefinitionMgr.getShapeItemFromShortKey(itemData);
}
case ColorItem.getId(): {
return COLOR_ITEM_SINGLETONS[itemData];
}
default: {
assertAlways(false, "Unknown item type: " + itemType);
}
}
}
export const typeItemSingleton = types.obj(gItemRegistry, itemResolverSingleton);

View File

@ -98,3 +98,13 @@ export class ColorItem extends BaseItem {
context.fill(); context.fill();
} }
} }
/**
* Singleton instances
* @type {Object<enumColors, ColorItem>}
*/
export const COLOR_ITEM_SINGLETONS = {};
for (const color in enumColors) {
COLOR_ITEM_SINGLETONS[color] = new ColorItem(color);
}

View File

@ -37,7 +37,6 @@ export class ShapeItem extends BaseItem {
*/ */
constructor(definition) { constructor(definition) {
super(); super();
// logger.log("New shape item for shape definition", definition.generateId(), "created");
/** /**
* This property must not be modified on runtime, you have to clone the class in order to change the definition * This property must not be modified on runtime, you have to clone the class in order to change the definition

View File

@ -6,7 +6,7 @@ import { Vector } from "../core/vector";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
import { Entity } from "./entity"; import { Entity } from "./entity";
import { ColorItem } from "./items/color_item"; import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
import { enumLayer, GameRoot } from "./root"; import { enumLayer, GameRoot } from "./root";
import { enumSubShape } from "./shape_definition"; import { enumSubShape } from "./shape_definition";
@ -139,7 +139,7 @@ export class MapChunk {
if (distanceToOriginInChunks > 2) { if (distanceToOriginInChunks > 2) {
availableColors.push(enumColors.blue); availableColors.push(enumColors.blue);
} }
this.internalGeneratePatch(rng, colorPatchSize, new ColorItem(rng.choice(availableColors))); this.internalGeneratePatch(rng, colorPatchSize, COLOR_ITEM_SINGLETONS[rng.choice(availableColors)]);
} }
/** /**
@ -268,7 +268,7 @@ export class MapChunk {
*/ */
generatePredefined(rng) { generatePredefined(rng) {
if (this.x === 0 && this.y === 0) { if (this.x === 0 && this.y === 0) {
this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.red), 7, 7); this.internalGeneratePatch(rng, 2, COLOR_ITEM_SINGLETONS[enumColors.red], 7, 7);
return true; return true;
} }
if (this.x === -1 && this.y === 0) { if (this.x === -1 && this.y === 0) {
@ -283,7 +283,7 @@ export class MapChunk {
} }
if (this.x === -1 && this.y === -1) { if (this.x === -1 && this.y === -1) {
this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.green)); this.internalGeneratePatch(rng, 2, COLOR_ITEM_SINGLETONS[enumColors.green]);
return true; return true;
} }

View File

@ -1,15 +1,14 @@
import { ConstantSignalComponent } from "../components/constant_signal"; import trim from "trim";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { Entity } from "../entity";
import { DialogWithForm } from "../../core/modal_dialog_elements"; import { DialogWithForm } from "../../core/modal_dialog_elements";
import { FormElementInput } from "../../core/modal_dialog_forms"; import { FormElementInput } from "../../core/modal_dialog_forms";
import { enumColors } from "../colors";
import { ColorItem } from "../items/color_item";
import trim from "trim";
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
import { ShapeDefinition } from "../shape_definition";
import { ShapeItem } from "../items/shape_item";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { enumColors } from "../colors";
import { ConstantSignalComponent } from "../components/constant_signal";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition";
export class ConstantSignalSystem extends GameSystemWithFilter { export class ConstantSignalSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -111,7 +110,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
const codeLower = code.toLowerCase(); const codeLower = code.toLowerCase();
if (enumColors[codeLower]) { if (enumColors[codeLower]) {
return new ColorItem(codeLower); return COLOR_ITEM_SINGLETONS[codeLower];
} }
if (code === "1" || codeLower === "true") { if (code === "1" || codeLower === "true") {
return BOOL_TRUE_SINGLETON; return BOOL_TRUE_SINGLETON;
@ -122,7 +121,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
} }
if (ShapeDefinition.isValidShortKey(code)) { if (ShapeDefinition.isValidShortKey(code)) {
return new ShapeItem(this.root.shapeDefinitionMgr.getShapeFromShortKey(code)); return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
} }
return null; return null;

View File

@ -1,11 +1,10 @@
import { GameSystemWithFilter } from "../game_system_with_filter";
import { HubComponent } from "../components/hub";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { Entity } from "../entity";
import { formatBigNumber } from "../../core/utils";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { formatBigNumber } from "../../core/utils";
import { T } from "../../translations"; import { T } from "../../translations";
import { ShapeItem } from "../items/shape_item"; import { HubComponent } from "../components/hub";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
export class HubSystem extends GameSystemWithFilter { export class HubSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -23,7 +22,9 @@ export class HubSystem extends GameSystemWithFilter {
// Set hub goal // Set hub goal
const entity = this.allEntities[i]; const entity = this.allEntities[i];
const pinsComp = entity.components.WiredPins; const pinsComp = entity.components.WiredPins;
pinsComp.slots[0].value = new ShapeItem(this.root.hubGoals.currentGoal.definition); pinsComp.slots[0].value = this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
this.root.hubGoals.currentGoal.definition
);
} }
} }

View File

@ -5,7 +5,7 @@ import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/it
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_TRUE_SINGLETON } from "../items/boolean_item"; import { BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { ColorItem } from "../items/color_item"; import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeItem } from "../items/shape_item"; import { ShapeItem } from "../items/shape_item";
export class ItemProcessorSystem extends GameSystemWithFilter { export class ItemProcessorSystem extends GameSystemWithFilter {
@ -134,7 +134,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const definition = cutDefinitions[i]; const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) { if (!definition.isEntirelyEmpty()) {
outItems.push({ outItems.push({
item: new ShapeItem(definition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i, requiredSlot: i,
}); });
} }
@ -155,7 +155,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const definition = cutDefinitions[i]; const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) { if (!definition.isEntirelyEmpty()) {
outItems.push({ outItems.push({
item: new ShapeItem(definition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i, requiredSlot: i,
}); });
} }
@ -172,7 +172,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition); const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
outItems.push({ outItems.push({
item: new ShapeItem(rotatedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
}); });
break; break;
} }
@ -185,7 +185,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition); const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition);
outItems.push({ outItems.push({
item: new ShapeItem(rotatedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
}); });
break; break;
} }
@ -198,7 +198,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition); const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition);
outItems.push({ outItems.push({
item: new ShapeItem(rotatedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
}); });
break; break;
} }
@ -217,7 +217,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
upperItem.definition upperItem.definition
); );
outItems.push({ outItems.push({
item: new ShapeItem(stackedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
}); });
break; break;
} }
@ -248,7 +248,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
resultColor = mixedColor; resultColor = mixedColor;
} }
outItems.push({ outItems.push({
item: new ColorItem(resultColor), item: COLOR_ITEM_SINGLETONS[resultColor],
}); });
break; break;
@ -266,7 +266,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
); );
outItems.push({ outItems.push({
item: new ShapeItem(colorizedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
}); });
break; break;
@ -293,11 +293,11 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
colorItem.color colorItem.color
); );
outItems.push({ outItems.push({
item: new ShapeItem(colorizedDefinition1), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
}); });
outItems.push({ outItems.push({
item: new ShapeItem(colorizedDefinition2), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
}); });
break; break;
@ -324,7 +324,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
); );
outItems.push({ outItems.push({
item: new ShapeItem(colorizedDefinition), item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
}); });
break; break;

View File

@ -1,8 +1,11 @@
import { createLogger } from "../core/logging";
import { import {
BaseDataType, BaseDataType,
TypeArray, TypeArray,
TypeBoolean, TypeBoolean,
TypeClass, TypeClass,
TypeClassData,
TypeClassFromMetaclass,
TypeClassId, TypeClassId,
TypeEntity, TypeEntity,
TypeEntityWeakref, TypeEntityWeakref,
@ -17,12 +20,9 @@ import {
TypePositiveInteger, TypePositiveInteger,
TypePositiveNumber, TypePositiveNumber,
TypeString, TypeString,
TypeVector,
TypeClassFromMetaclass,
TypeClassData,
TypeStructuredObject, TypeStructuredObject,
TypeVector,
} from "./serialization_data_types"; } from "./serialization_data_types";
import { createLogger } from "../core/logging";
const logger = createLogger("serialization"); const logger = createLogger("serialization");
@ -69,9 +69,10 @@ export const types = {
/** /**
* @param {FactoryTemplate<*>} registry * @param {FactoryTemplate<*>} registry
* @param {(GameRoot, any) => object=} resolver
*/ */
obj(registry) { obj(registry, resolver = null) {
return new TypeClass(registry); return new TypeClass(registry, resolver);
}, },
/** /**
@ -190,12 +191,18 @@ export class BasicSerializableObject {
); );
} }
/** @returns {string|void} */ /**
deserialize(data) { * @param {any} data
* @param {import("./savegame_serializer").GameRoot} root
* @returns {string|void}
*/
deserialize(data, root = null) {
return deserializeSchema( return deserializeSchema(
this, this,
/** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(), /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(),
data data,
null,
root
); );
} }
@ -253,9 +260,10 @@ export function serializeSchema(obj, schema, mergeWith = {}) {
* @param {Schema} schema The schema to use * @param {Schema} schema The schema to use
* @param {object} data The serialized data * @param {object} data The serialized data
* @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it
* @param {import("../game/root").GameRoot=} root Optional game root reference
* @returns {string|void} String error code or nothing on success * @returns {string|void} String error code or nothing on success
*/ */
export function deserializeSchema(obj, schema, data, baseclassErrorResult = null) { export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) {
if (baseclassErrorResult) { if (baseclassErrorResult) {
return baseclassErrorResult; return baseclassErrorResult;
} }
@ -275,7 +283,7 @@ export function deserializeSchema(obj, schema, data, baseclassErrorResult = null
return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name; return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name;
} }
const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root); const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root);
if (errorStatus) { if (errorStatus) {
logger.error( logger.error(
"Deserialization failed with error '" + errorStatus + "' on object", "Deserialization failed with error '" + errorStatus + "' on object",

View File

@ -595,10 +595,12 @@ export class TypeClass extends BaseDataType {
/** /**
* *
* @param {FactoryTemplate<*>} registry * @param {FactoryTemplate<*>} registry
* @param {(GameRoot, object) => object} customResolver
*/ */
constructor(registry) { constructor(registry, customResolver = null) {
super(); super();
this.registry = registry; this.registry = registry;
this.customResolver = customResolver;
} }
serialize(value) { serialize(value) {
@ -640,14 +642,23 @@ export class TypeClass extends BaseDataType {
* @returns {string|void} String error code or null on success * @returns {string|void} String error code or null on success
*/ */
deserialize(value, targetObject, targetKey, root) { deserialize(value, targetObject, targetKey, root) {
const instanceClass = this.registry.findById(value.$); let instance;
if (!instanceClass || !instanceClass.prototype) {
return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; if (this.customResolver) {
} instance = this.customResolver(root, value);
const instance = Object.create(instanceClass.prototype); if (!instance) {
const errorState = instance.deserialize(value.data); return "Failed to call custom resolver";
if (errorState) { }
return errorState; } else {
const instanceClass = this.registry.findById(value.$);
if (!instanceClass || !instanceClass.prototype) {
return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass;
}
instance = Object.create(instanceClass.prototype);
const errorState = instance.deserialize(value.data);
if (errorState) {
return errorState;
}
} }
targetObject[targetKey] = instance; targetObject[targetKey] = instance;
} }

View File

@ -60,7 +60,7 @@ export class SerializerInternal {
entity.uid = payload.uid; entity.uid = payload.uid;
this.deserializeComponents(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);
@ -70,18 +70,19 @@ export class SerializerInternal {
/** /**
* Deserializes components of an entity * Deserializes components of an entity
* @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(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]); const errorStatus = entity.components[componentId].deserialize(data[componentId], root);
if (errorStatus) { if (errorStatus) {
return errorStatus; return errorStatus;
} }