mirror of
				https://github.com/tobspr/shapez.io.git
				synced 2025-06-13 13:04:03 +00:00 
			
		
		
		
	Optimize performance by using singletons for items
This commit is contained in:
		
							parent
							
								
									3c34227c24
								
							
						
					
					
						commit
						8c39d31c5b
					
				| @ -1,14 +1,14 @@ | ||||
| import { globalConfig } from "../core/config"; | ||||
| import { DrawParameters } from "../core/draw_parameters"; | ||||
| import { gItemRegistry } from "../core/global_registries"; | ||||
| import { createLogger } from "../core/logging"; | ||||
| import { Rectangle } from "../core/rectangle"; | ||||
| 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 { BaseItem } from "./base_item"; | ||||
| import { Entity } from "./entity"; | ||||
| import { GameRoot, enumLayer } from "./root"; | ||||
| import { typeItemSingleton } from "./item_resolver"; | ||||
| import { enumLayer, GameRoot } from "./root"; | ||||
| 
 | ||||
| const logger = createLogger("belt_path"); | ||||
| 
 | ||||
| @ -29,7 +29,7 @@ export class BeltPath extends BasicSerializableObject { | ||||
|     static getSchema() { | ||||
|         return { | ||||
|             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, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { gItemRegistry } from "../../core/global_registries"; | ||||
| import { types } from "../../savegame/serialization"; | ||||
| import { Component } from "../component"; | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| export class ConstantSignalComponent extends Component { | ||||
|     static getId() { | ||||
| @ -10,7 +11,7 @@ export class ConstantSignalComponent extends Component { | ||||
| 
 | ||||
|     static getSchema() { | ||||
|         return { | ||||
|             signal: types.nullable(types.obj(gItemRegistry)), | ||||
|             signal: types.nullable(typeItemSingleton), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| import { Vector, enumDirection, enumDirectionToVector } from "../../core/vector"; | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { Component } from "../component"; | ||||
| import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector"; | ||||
| import { types } from "../../savegame/serialization"; | ||||
| import { gItemRegistry } from "../../core/global_registries"; | ||||
| import { Entity } from "../entity"; | ||||
| import { enumLayer } from "../root"; | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { BeltPath } from "../belt_path"; | ||||
| import { Component } from "../component"; | ||||
| import { Entity } from "../entity"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| /** | ||||
|  * @typedef {{ | ||||
| @ -29,7 +28,7 @@ export class ItemEjectorComponent extends Component { | ||||
|         return { | ||||
|             slots: types.array( | ||||
|                 types.structured({ | ||||
|                     item: types.nullable(types.obj(gItemRegistry)), | ||||
|                     item: types.nullable(typeItemSingleton), | ||||
|                     progress: types.float, | ||||
|                 }) | ||||
|             ), | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { gItemRegistry } from "../../core/global_registries"; | ||||
| import { types } from "../../savegame/serialization"; | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { Component } from "../component"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| /** @enum {string} */ | ||||
| export const enumItemProcessorTypes = { | ||||
| @ -32,13 +32,13 @@ export class ItemProcessorComponent extends Component { | ||||
|             nextOutputSlot: types.uint, | ||||
|             inputSlots: types.array( | ||||
|                 types.structured({ | ||||
|                     item: types.obj(gItemRegistry), | ||||
|                     item: typeItemSingleton, | ||||
|                     sourceSlot: types.uint, | ||||
|                 }) | ||||
|             ), | ||||
|             itemsToEject: types.array( | ||||
|                 types.structured({ | ||||
|                     item: types.obj(gItemRegistry), | ||||
|                     item: typeItemSingleton, | ||||
|                     requiredSlot: types.nullable(types.uint), | ||||
|                     preferredSlot: types.nullable(types.uint), | ||||
|                 }) | ||||
|  | ||||
| @ -1,8 +1,7 @@ | ||||
| import { globalConfig } from "../../core/config"; | ||||
| import { types } from "../../savegame/serialization"; | ||||
| import { Component } from "../component"; | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { gItemRegistry } from "../../core/global_registries"; | ||||
| import { Component } from "../component"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| const chainBufferSize = 3; | ||||
| 
 | ||||
| @ -15,7 +14,7 @@ export class MinerComponent extends Component { | ||||
|         // cachedMinedItem is not serialized.
 | ||||
|         return { | ||||
|             lastMiningTime: types.ufloat, | ||||
|             itemChainBuffer: types.array(types.obj(gItemRegistry)), | ||||
|             itemChainBuffer: types.array(typeItemSingleton), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,9 +1,7 @@ | ||||
| import { Component } from "../component"; | ||||
| import { types } from "../../savegame/serialization"; | ||||
| import { gItemRegistry } from "../../core/global_registries"; | ||||
| import { BaseItem, enumItemType } from "../base_item"; | ||||
| import { ColorItem } from "../items/color_item"; | ||||
| import { ShapeItem } from "../items/shape_item"; | ||||
| import { Component } from "../component"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| export class StorageComponent extends Component { | ||||
|     static getId() { | ||||
| @ -13,7 +11,7 @@ export class StorageComponent extends Component { | ||||
|     static getSchema() { | ||||
|         return { | ||||
|             storedCount: types.uint, | ||||
|             storedItem: types.nullable(types.obj(gItemRegistry)), | ||||
|             storedItem: types.nullable(typeItemSingleton), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| import { BaseItem } from "../base_item"; | ||||
| import { Component } from "../component"; | ||||
| import { globalConfig } from "../../core/config"; | ||||
| 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 { enumLayer } from "../root"; | ||||
| import { typeItemSingleton } from "../item_resolver"; | ||||
| 
 | ||||
| /** @enum {string} */ | ||||
| export const enumUndergroundBeltMode = { | ||||
| @ -26,7 +25,7 @@ export class UndergroundBeltComponent extends Component { | ||||
| 
 | ||||
|     static getSchema() { | ||||
|         return { | ||||
|             pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.float)), | ||||
|             pendingItems: types.array(types.pair(typeItemSingleton, types.float)), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -13,7 +13,6 @@ import { randomInt, round2Digits } from "../core/utils"; | ||||
| import { Vector } from "../core/vector"; | ||||
| import { Savegame } from "../savegame/savegame"; | ||||
| import { SavegameSerializer } from "../savegame/savegame_serializer"; | ||||
| import { InGameState } from "../states/ingame"; | ||||
| import { AutomaticSave } from "./automatic_save"; | ||||
| import { MetaHubBuilding } from "./buildings/hub"; | ||||
| import { Camera } from "./camera"; | ||||
| @ -67,7 +66,7 @@ export class GameCore { | ||||
|     /** | ||||
|      * Initializes the root object which stores all game related data. The state | ||||
|      * is required as a back reference (used sometimes) | ||||
|      * @param {InGameState} parentState | ||||
|      * @param {import("../states/ingame").InGameState} parentState | ||||
|      * @param {Savegame} savegame | ||||
|      */ | ||||
|     initializeRoot(parentState, savegame) { | ||||
|  | ||||
							
								
								
									
										33
									
								
								src/js/game/item_resolver.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/js/game/item_resolver.js
									
									
									
									
									
										Normal 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); | ||||
| @ -98,3 +98,13 @@ export class ColorItem extends BaseItem { | ||||
|         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); | ||||
| } | ||||
|  | ||||
| @ -37,7 +37,6 @@ export class ShapeItem extends BaseItem { | ||||
|      */ | ||||
|     constructor(definition) { | ||||
|         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 | ||||
|  | ||||
| @ -6,7 +6,7 @@ import { Vector } from "../core/vector"; | ||||
| import { BaseItem } from "./base_item"; | ||||
| import { enumColors } from "./colors"; | ||||
| import { Entity } from "./entity"; | ||||
| import { ColorItem } from "./items/color_item"; | ||||
| import { COLOR_ITEM_SINGLETONS } from "./items/color_item"; | ||||
| import { enumLayer, GameRoot } from "./root"; | ||||
| import { enumSubShape } from "./shape_definition"; | ||||
| 
 | ||||
| @ -139,7 +139,7 @@ export class MapChunk { | ||||
|         if (distanceToOriginInChunks > 2) { | ||||
|             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) { | ||||
|         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; | ||||
|         } | ||||
|         if (this.x === -1 && this.y === 0) { | ||||
| @ -283,7 +283,7 @@ export class MapChunk { | ||||
|         } | ||||
| 
 | ||||
|         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; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -1,15 +1,14 @@ | ||||
| import { ConstantSignalComponent } from "../components/constant_signal"; | ||||
| import { GameSystemWithFilter } from "../game_system_with_filter"; | ||||
| import { Entity } from "../entity"; | ||||
| import trim from "trim"; | ||||
| import { DialogWithForm } from "../../core/modal_dialog_elements"; | ||||
| 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 { 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 { | ||||
|     constructor(root) { | ||||
| @ -111,7 +110,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter { | ||||
|         const codeLower = code.toLowerCase(); | ||||
| 
 | ||||
|         if (enumColors[codeLower]) { | ||||
|             return new ColorItem(codeLower); | ||||
|             return COLOR_ITEM_SINGLETONS[codeLower]; | ||||
|         } | ||||
|         if (code === "1" || codeLower === "true") { | ||||
|             return BOOL_TRUE_SINGLETON; | ||||
| @ -122,7 +121,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter { | ||||
|         } | ||||
| 
 | ||||
|         if (ShapeDefinition.isValidShortKey(code)) { | ||||
|             return new ShapeItem(this.root.shapeDefinitionMgr.getShapeFromShortKey(code)); | ||||
|             return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| import { GameSystemWithFilter } from "../game_system_with_filter"; | ||||
| import { HubComponent } from "../components/hub"; | ||||
| import { DrawParameters } from "../../core/draw_parameters"; | ||||
| import { Entity } from "../entity"; | ||||
| import { formatBigNumber } from "../../core/utils"; | ||||
| import { Loader } from "../../core/loader"; | ||||
| import { formatBigNumber } from "../../core/utils"; | ||||
| 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 { | ||||
|     constructor(root) { | ||||
| @ -23,7 +22,9 @@ export class HubSystem extends GameSystemWithFilter { | ||||
|             // Set hub goal
 | ||||
|             const entity = this.allEntities[i]; | ||||
|             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 | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/it | ||||
| import { Entity } from "../entity"; | ||||
| import { GameSystemWithFilter } from "../game_system_with_filter"; | ||||
| 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"; | ||||
| 
 | ||||
| export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
| @ -134,7 +134,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                     const definition = cutDefinitions[i]; | ||||
|                     if (!definition.isEntirelyEmpty()) { | ||||
|                         outItems.push({ | ||||
|                             item: new ShapeItem(definition), | ||||
|                             item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), | ||||
|                             requiredSlot: i, | ||||
|                         }); | ||||
|                     } | ||||
| @ -155,7 +155,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                     const definition = cutDefinitions[i]; | ||||
|                     if (!definition.isEntirelyEmpty()) { | ||||
|                         outItems.push({ | ||||
|                             item: new ShapeItem(definition), | ||||
|                             item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition), | ||||
|                             requiredSlot: i, | ||||
|                         }); | ||||
|                     } | ||||
| @ -172,7 +172,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
| 
 | ||||
|                 const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition); | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(rotatedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), | ||||
|                 }); | ||||
|                 break; | ||||
|             } | ||||
| @ -185,7 +185,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
| 
 | ||||
|                 const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition); | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(rotatedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), | ||||
|                 }); | ||||
|                 break; | ||||
|             } | ||||
| @ -198,7 +198,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
| 
 | ||||
|                 const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition); | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(rotatedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition), | ||||
|                 }); | ||||
|                 break; | ||||
|             } | ||||
| @ -217,7 +217,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                     upperItem.definition | ||||
|                 ); | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(stackedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition), | ||||
|                 }); | ||||
|                 break; | ||||
|             } | ||||
| @ -248,7 +248,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                     resultColor = mixedColor; | ||||
|                 } | ||||
|                 outItems.push({ | ||||
|                     item: new ColorItem(resultColor), | ||||
|                     item: COLOR_ITEM_SINGLETONS[resultColor], | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
| @ -266,7 +266,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                 ); | ||||
| 
 | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(colorizedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
| @ -293,11 +293,11 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                     colorItem.color | ||||
|                 ); | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(colorizedDefinition1), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1), | ||||
|                 }); | ||||
| 
 | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(colorizedDefinition2), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2), | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
| @ -324,7 +324,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { | ||||
|                 ); | ||||
| 
 | ||||
|                 outItems.push({ | ||||
|                     item: new ShapeItem(colorizedDefinition), | ||||
|                     item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition), | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
|  | ||||
| @ -1,8 +1,11 @@ | ||||
| import { createLogger } from "../core/logging"; | ||||
| import { | ||||
|     BaseDataType, | ||||
|     TypeArray, | ||||
|     TypeBoolean, | ||||
|     TypeClass, | ||||
|     TypeClassData, | ||||
|     TypeClassFromMetaclass, | ||||
|     TypeClassId, | ||||
|     TypeEntity, | ||||
|     TypeEntityWeakref, | ||||
| @ -17,12 +20,9 @@ import { | ||||
|     TypePositiveInteger, | ||||
|     TypePositiveNumber, | ||||
|     TypeString, | ||||
|     TypeVector, | ||||
|     TypeClassFromMetaclass, | ||||
|     TypeClassData, | ||||
|     TypeStructuredObject, | ||||
|     TypeVector, | ||||
| } from "./serialization_data_types"; | ||||
| import { createLogger } from "../core/logging"; | ||||
| 
 | ||||
| const logger = createLogger("serialization"); | ||||
| 
 | ||||
| @ -69,9 +69,10 @@ export const types = { | ||||
| 
 | ||||
|     /** | ||||
|      * @param {FactoryTemplate<*>} registry | ||||
|      * @param {(GameRoot, any) => object=} resolver | ||||
|      */ | ||||
|     obj(registry) { | ||||
|         return new TypeClass(registry); | ||||
|     obj(registry, resolver = null) { | ||||
|         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( | ||||
|             this, | ||||
|             /** @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 {object} data The serialized data | ||||
|  * @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 | ||||
|  */ | ||||
| export function deserializeSchema(obj, schema, data, baseclassErrorResult = null) { | ||||
| export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) { | ||||
|     if (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; | ||||
|         } | ||||
| 
 | ||||
|         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) { | ||||
|             logger.error( | ||||
|                 "Deserialization failed with error '" + errorStatus + "' on object", | ||||
|  | ||||
| @ -595,10 +595,12 @@ export class TypeClass extends BaseDataType { | ||||
|     /** | ||||
|      * | ||||
|      * @param {FactoryTemplate<*>} registry | ||||
|      * @param {(GameRoot, object) => object} customResolver | ||||
|      */ | ||||
|     constructor(registry) { | ||||
|     constructor(registry, customResolver = null) { | ||||
|         super(); | ||||
|         this.registry = registry; | ||||
|         this.customResolver = customResolver; | ||||
|     } | ||||
| 
 | ||||
|     serialize(value) { | ||||
| @ -640,15 +642,24 @@ export class TypeClass extends BaseDataType { | ||||
|      * @returns {string|void} String error code or null on success | ||||
|      */ | ||||
|     deserialize(value, targetObject, targetKey, root) { | ||||
|         let instance; | ||||
| 
 | ||||
|         if (this.customResolver) { | ||||
|             instance = this.customResolver(root, value); | ||||
|             if (!instance) { | ||||
|                 return "Failed to call custom resolver"; | ||||
|             } | ||||
|         } else { | ||||
|             const instanceClass = this.registry.findById(value.$); | ||||
|             if (!instanceClass || !instanceClass.prototype) { | ||||
|                 return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; | ||||
|             } | ||||
|         const instance = Object.create(instanceClass.prototype); | ||||
|             instance = Object.create(instanceClass.prototype); | ||||
|             const errorState = instance.deserialize(value.data); | ||||
|             if (errorState) { | ||||
|                 return errorState; | ||||
|             } | ||||
|         } | ||||
|         targetObject[targetKey] = instance; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -60,7 +60,7 @@ export class SerializerInternal { | ||||
| 
 | ||||
|         entity.uid = payload.uid; | ||||
| 
 | ||||
|         this.deserializeComponents(entity, payload.components); | ||||
|         this.deserializeComponents(root, entity, payload.components); | ||||
| 
 | ||||
|         root.entityMgr.registerEntity(entity, payload.uid); | ||||
|         root.map.placeStaticEntity(entity); | ||||
| @ -70,18 +70,19 @@ export class SerializerInternal { | ||||
| 
 | ||||
|     /** | ||||
|      * Deserializes components of an entity | ||||
|      * @param {GameRoot} root | ||||
|      * @param {Entity} entity | ||||
|      * @param {Object.<string, any>} data | ||||
|      * @returns {string|void} | ||||
|      */ | ||||
|     deserializeComponents(entity, data) { | ||||
|     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]); | ||||
|             const errorStatus = entity.components[componentId].deserialize(data[componentId], root); | ||||
|             if (errorStatus) { | ||||
|                 return errorStatus; | ||||
|             } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user