Only store changed properties for all components

This commit is contained in:
tobspr 2020-08-10 22:53:02 +02:00
parent bb431b8490
commit 9701a143ec
22 changed files with 100 additions and 152 deletions

View File

@ -3,12 +3,9 @@ import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector";
import { SOUNDS } from "../../platform/sound";
import { T } from "../../translations";
import { BeltComponent } from "../components/belt";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { ReplaceableMapEntityComponent } from "../components/replaceable_map_entity";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { GameRoot, enumLayer } from "../root";
import { GameRoot } from "../root";
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
@ -43,6 +40,10 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
return null;
}
getIsReplaceable() {
return true;
}
/**
* Creates the entity at the given location
* @param {Entity} entity
@ -53,8 +54,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
direction: enumDirection.top, // updated later
})
);
// Make this entity replaceable
entity.addComponent(new ReplaceableMapEntityComponent());
}
/**

View File

@ -3,7 +3,6 @@ import { enumItemType } from "../base_item";
import { HubComponent } from "../components/hub";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { UnremovableComponent } from "../components/unremovable";
import { Entity } from "../entity";
import { MetaBuilding } from "../meta_building";
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
@ -34,6 +33,10 @@ export class MetaHubBuilding extends MetaBuilding {
return null;
}
getIsRemovable() {
return false;
}
/**
* Creates the entity at the given location
* @param {Entity} entity
@ -47,8 +50,6 @@ export class MetaHubBuilding extends MetaBuilding {
})
);
entity.addComponent(new UnremovableComponent());
entity.addComponent(
new WiredPinsComponent({
slots: [

View File

@ -5,9 +5,7 @@ import { ItemEjectorComponent } from "./components/item_ejector";
import { ItemAcceptorComponent } from "./components/item_acceptor";
import { MinerComponent } from "./components/miner";
import { ItemProcessorComponent } from "./components/item_processor";
import { ReplaceableMapEntityComponent } from "./components/replaceable_map_entity";
import { UndergroundBeltComponent } from "./components/underground_belt";
import { UnremovableComponent } from "./components/unremovable";
import { HubComponent } from "./components/hub";
import { StorageComponent } from "./components/storage";
import { WiredPinsComponent } from "./components/wired_pins";
@ -20,9 +18,7 @@ export function initComponentRegistry() {
gComponentRegistry.register(ItemAcceptorComponent);
gComponentRegistry.register(MinerComponent);
gComponentRegistry.register(ItemProcessorComponent);
gComponentRegistry.register(ReplaceableMapEntityComponent);
gComponentRegistry.register(UndergroundBeltComponent);
gComponentRegistry.register(UnremovableComponent);
gComponentRegistry.register(HubComponent);
gComponentRegistry.register(StorageComponent);
gComponentRegistry.register(WiredPinsComponent);

View File

@ -40,12 +40,6 @@ export class BeltComponent extends Component {
return "Belt";
}
static getSchema() {
return {
direction: types.string,
};
}
duplicateWithoutContents() {
return new BeltComponent({ direction: this.direction });
}

View File

@ -7,17 +7,6 @@ export class BeltUnderlaysComponent extends Component {
return "BeltUnderlays";
}
static getSchema() {
return {
underlays: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
})
),
};
}
duplicateWithoutContents() {
const beltUnderlaysCopy = [];
for (let i = 0; i < this.underlays.length; ++i) {

View File

@ -28,18 +28,6 @@ export class ItemAcceptorComponent extends Component {
return "ItemAcceptor";
}
static getSchema() {
return {
slots: types.array(
types.structured({
pos: types.vector,
directions: types.array(types.enum(enumDirection)),
filter: types.nullable(types.enum(enumItemType)),
})
),
};
}
duplicateWithoutContents() {
const slotsCopy = [];
for (let i = 0; i < this.slots.length; ++i) {

View File

@ -29,8 +29,6 @@ export class ItemEjectorComponent extends Component {
return {
slots: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
item: types.nullable(types.obj(gItemRegistry)),
progress: types.float,
})

View File

@ -29,9 +29,6 @@ export class ItemProcessorComponent extends Component {
static getSchema() {
return {
nextOutputSlot: types.uint,
type: types.enum(enumItemProcessorTypes),
inputsPerCharge: types.uint,
inputSlots: types.array(
types.structured({
item: types.obj(gItemRegistry),

View File

@ -15,7 +15,6 @@ export class MinerComponent extends Component {
// cachedMinedItem is not serialized.
return {
lastMiningTime: types.ufloat,
chainable: types.bool,
itemChainBuffer: types.array(types.obj(gItemRegistry)),
};
}

View File

@ -1,15 +0,0 @@
import { Component } from "../component";
/**
* Marks an entity as replaceable, so that when other buildings are placed above him it
* simply gets deleted
*/
export class ReplaceableMapEntityComponent extends Component {
static getId() {
return "ReplaceableMapEntity";
}
duplicateWithoutContents() {
return new ReplaceableMapEntityComponent();
}
}

View File

@ -6,6 +6,7 @@ import { enumDirection, Vector } from "../../core/vector";
import { types } from "../../savegame/serialization";
import { Component } from "../component";
import { getBuildingDataFromCode } from "../building_codes";
import { MetaBuilding } from "../meta_building";
export class StaticMapEntityComponent extends Component {
static getId() {
@ -15,7 +16,6 @@ export class StaticMapEntityComponent extends Component {
static getSchema() {
return {
origin: types.tileVector,
tileSize: types.tileVector,
rotation: types.float,
originalRotation: types.float,
@ -56,6 +56,14 @@ export class StaticMapEntityComponent extends Component {
return getBuildingDataFromCode(this.code).silhouetteColor;
}
/**
* Returns the meta building
* @returns {MetaBuilding}
*/
getMetaBuilding() {
return getBuildingDataFromCode(this.code).metaInstance;
}
duplicateWithoutContents() {
return new StaticMapEntityComponent({
origin: this.origin.copy(),

View File

@ -12,10 +12,8 @@ export class StorageComponent extends Component {
static getSchema() {
return {
maximumStorage: types.uint,
storedCount: types.uint,
storedItem: types.nullable(types.obj(gItemRegistry)),
overlayOpacity: types.ufloat,
};
}

View File

@ -26,9 +26,7 @@ export class UndergroundBeltComponent extends Component {
static getSchema() {
return {
mode: types.enum(enumUndergroundBeltMode),
pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.float)),
tier: types.uint,
};
}

View File

@ -1,15 +0,0 @@
import { Component } from "../component";
export class UnremovableComponent extends Component {
static getId() {
return "Unremovable";
}
static getSchema() {
return {};
}
duplicateWithoutContents() {
return new UnremovableComponent();
}
}

View File

@ -29,17 +29,6 @@ export class WiredPinsComponent extends Component {
return "WiredPins";
}
static getSchema() {
return {
slots: types.array(
types.structured({
pos: types.vector,
type: types.enum(enumPinSlotType),
})
),
};
}
/**
*
* @param {object} param0

View File

@ -76,8 +76,7 @@ export class Entity extends BasicSerializableObject {
static getSchema() {
return {
uid: types.uint,
components: types.keyValueMap(types.objData(gComponentRegistry)),
layer: types.enum(enumLayer),
components: types.keyValueMap(types.objData(gComponentRegistry), false),
};
}

View File

@ -1,19 +1,15 @@
/* typehints:start */
import { StaticMapEntityComponent } from "./components/static_map_entity";
import { BeltComponent } from "./components/belt";
import { ItemEjectorComponent } from "./components/item_ejector";
import { ItemAcceptorComponent } from "./components/item_acceptor";
import { MinerComponent } from "./components/miner";
import { ItemProcessorComponent } from "./components/item_processor";
import { ReplaceableMapEntityComponent } from "./components/replaceable_map_entity";
import { UndergroundBeltComponent } from "./components/underground_belt";
import { UnremovableComponent } from "./components/unremovable";
import { HubComponent } from "./components/hub";
import { StorageComponent } from "./components/storage";
import { EnergyGeneratorComponent } from "./components/energy_generator";
import { WiredPinsComponent } from "./components/wired_pins";
import { EnergyConsumerComponent } from "./components/energy_consumer";
import { BeltUnderlaysComponent } from "./components/belt_underlays";
import { HubComponent } from "./components/hub";
import { ItemAcceptorComponent } from "./components/item_acceptor";
import { ItemEjectorComponent } from "./components/item_ejector";
import { ItemProcessorComponent } from "./components/item_processor";
import { MinerComponent } from "./components/miner";
import { StaticMapEntityComponent } from "./components/static_map_entity";
import { StorageComponent } from "./components/storage";
import { UndergroundBeltComponent } from "./components/underground_belt";
import { WiredPinsComponent } from "./components/wired_pins";
/* typehints:end */
/**
@ -42,30 +38,18 @@ export class EntityComponentStorage {
/** @type {ItemProcessorComponent} */
this.ItemProcessor;
/** @type {ReplaceableMapEntityComponent} */
this.ReplaceableMapEntity;
/** @type {UndergroundBeltComponent} */
this.UndergroundBelt;
/** @type {UnremovableComponent} */
this.Unremovable;
/** @type {HubComponent} */
this.Hub;
/** @type {StorageComponent} */
this.Storage;
/** @type {EnergyGeneratorComponent} */
this.EnergyGenerator;
/** @type {WiredPinsComponent} */
this.WiredPins;
/** @type {EnergyConsumerComponent} */
this.EnergyConsumer;
/** @type {BeltUnderlaysComponent} */
this.BeltUnderlays;

View File

@ -63,9 +63,12 @@ export class GameLogic {
for (let y = rect.y; y < rect.y + rect.h; ++y) {
// Check if there is any direct collision
const otherEntity = this.root.map.getLayerContentXY(x, y, entity.layer);
if (otherEntity && !otherEntity.components.ReplaceableMapEntity) {
// This one is a direct blocker
return false;
if (otherEntity) {
const metaClass = otherEntity.components.StaticMapEntity.getMetaBuilding();
if (!metaClass.getIsReplaceable()) {
// This one is a direct blocker
return false;
}
}
}
}
@ -121,7 +124,7 @@ export class GameLogic {
const contents = this.root.map.getLayerContentXY(x, y, entity.layer);
if (contents) {
assertAlways(
contents.components.ReplaceableMapEntity,
contents.components.StaticMapEntity.getMetaBuilding().getIsReplaceable(),
"Tried to replace non-repleaceable entity"
);
if (!this.tryDeleteBuilding(contents)) {
@ -158,7 +161,8 @@ export class GameLogic {
* @param {Entity} building
*/
canDeleteBuilding(building) {
return building.components.StaticMapEntity && !building.components.Unremovable;
const staticComp = building.components.StaticMapEntity;
return staticComp.getMetaBuilding().getIsRemovable();
}
/**

View File

@ -64,6 +64,13 @@ export class MetaBuilding {
return [];
}
/**
* Returns whether this building can get replaced
*/
getIsReplaceable() {
return false;
}
/**
* Whether to flip the orientation after a building has been placed - useful
* for tunnels.
@ -80,6 +87,14 @@ export class MetaBuilding {
return false;
}
/**
* Returns whether this building is removable
* @returns {boolean}
*/
getIsRemovable() {
return true;
}
/**
* Returns the placement sound
* @returns {string}

View File

@ -52,7 +52,7 @@ export class WiredPinsSystem extends GameSystemWithFilter {
continue;
}
if (otherEntity.components.ReplaceableMapEntity) {
if (staticComp.getMetaBuilding().getIsReplaceable()) {
// Don't mind here, even if there would be a collision we
// could replace it
continue;
@ -105,8 +105,10 @@ export class WiredPinsSystem extends GameSystemWithFilter {
const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, enumLayer.wires);
// If there's an entity, and it can't get removed -> That's a collision
if (collidingEntity && !collidingEntity.components.ReplaceableMapEntity) {
return true;
if (collidingEntity) {
if (!collidingEntity.components.StaticMapEntity.getMetaBuilding().getIsReplaceable()) {
return true;
}
}
}
return false;
@ -130,7 +132,7 @@ export class WiredPinsSystem extends GameSystemWithFilter {
const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, enumLayer.wires);
if (collidingEntity) {
assertAlways(
collidingEntity.components.ReplaceableMapEntity,
collidingEntity.components.StaticMapEntity.getMetaBuilding().getIsReplaceable(),
"Tried to replace non-repleaceable entity for pins"
);
if (!this.root.logic.tryDeleteBuilding(collidingEntity)) {

View File

@ -871,14 +871,17 @@ export class TypeArray extends BaseDataType {
* @returns {string|void} String error code or null on success
*/
deserialize(value, targetObject, targetKey, root) {
const result = new Array(value.length);
let destination = targetObject[targetKey];
if (!destination) {
targetObject[targetKey] = destination = new Array(value.length);
}
for (let i = 0; i < value.length; ++i) {
const errorStatus = this.innerType.deserializeWithVerify(value[i], result, i, root);
const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root);
if (errorStatus) {
return errorStatus;
}
}
targetObject[targetKey] = result;
}
getAsJsonSchemaUncached() {
@ -1226,15 +1229,18 @@ export class TypeStructuredObject extends BaseDataType {
* @returns {string|void} String error code or null on success
*/
deserialize(value, targetObject, targetKey, root) {
let result = {};
let target = targetObject[targetKey];
if (!target) {
targetObject[targetKey] = target = {};
}
for (const key in value) {
const valueType = this.descriptor[key];
const errorCode = valueType.deserializeWithVerify(value[key], result, key, root);
const errorCode = valueType.deserializeWithVerify(value[key], target, key, root);
if (errorCode) {
return errorCode;
}
}
targetObject[targetKey] = result;
}
getAsJsonSchemaUncached() {

View File

@ -1,6 +1,10 @@
import { gComponentRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging";
import { Vector } from "../core/vector";
import { getBuildingDataFromCode } from "../game/building_codes";
import { Entity } from "../game/entity";
import { enumLayer, GameRoot } from "../game/root";
import { GameRoot } from "../game/root";
const logger = createLogger("serializer_internal");
// Internal serializer methods
export class SerializerInternal {
@ -37,19 +41,27 @@ export class SerializerInternal {
* @param {Entity} payload
*/
deserializeEntity(root, payload) {
const entity = new Entity(root);
this.deserializeComponents(entity, payload.components);
entity.layer = payload.layer;
const staticData = payload.components.StaticMapEntity;
assert(staticData, "entity has no static data");
if (!enumLayer[payload.layer]) {
assert(false, "Invalid layer: " + payload.layer);
}
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,
});
this.deserializeComponents(entity, payload.components);
root.entityMgr.registerEntity(entity, payload.uid);
if (entity.components.StaticMapEntity) {
root.map.placeStaticEntity(entity);
}
root.map.placeStaticEntity(entity);
}
/////// COMPONENTS ////
@ -62,10 +74,12 @@ export class SerializerInternal {
*/
deserializeComponents(entity, data) {
for (const componentId in data) {
const componentClass = gComponentRegistry.findById(componentId);
const componentHandle = new componentClass({});
entity.addComponent(componentHandle);
const errorStatus = componentHandle.deserialize(data[componentId]);
if (!entity.components[componentId]) {
logger.warn("Entity no longer has component:", componentId);
continue;
}
const errorStatus = entity.components[componentId].deserialize(data[componentId]);
if (errorStatus) {
return errorStatus;
}