Only store changed properties for all components
This commit is contained in:
parent
bb431b8490
commit
9701a143ec
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -40,12 +40,6 @@ export class BeltComponent extends Component {
|
|||
return "Belt";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
direction: types.string,
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new BeltComponent({ direction: this.direction });
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { Component } from "../component";
|
||||
|
||||
export class UnremovableComponent extends Component {
|
||||
static getId() {
|
||||
return "Unremovable";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new UnremovableComponent();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -63,12 +63,15 @@ 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) {
|
||||
if (otherEntity) {
|
||||
const metaClass = otherEntity.components.StaticMapEntity.getMetaBuilding();
|
||||
if (!metaClass.getIsReplaceable()) {
|
||||
// This one is a direct blocker
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform additional placement checks
|
||||
if (this.root.signals.prePlacementCheck.dispatch(entity, offset) === STOP_PROPAGATION) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,10 +105,12 @@ 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) {
|
||||
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)) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,20 +41,28 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/////// 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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue