Improve item ejector system

pull/670/head
tobspr 4 years ago
parent c0c624135b
commit 746f4935ad

@ -40,9 +40,9 @@ export class StaleAreaDetector {
* Makes this detector recompute the area of an entity whenever * Makes this detector recompute the area of an entity whenever
* it changes in any way * it changes in any way
* @param {Array<typeof Component>} components * @param {Array<typeof Component>} components
* @param {number} tilesAround * @param {number} tilesAround How many tiles arround to expand the area
*/ */
recomputeOnComponentsChanged(components, tilesAround = 1) { recomputeOnComponentsChanged(components, tilesAround) {
const componentIds = components.map(component => component.getId()); const componentIds = components.map(component => component.getId());
/** /**
@ -50,6 +50,10 @@ export class StaleAreaDetector {
* @param {Entity} entity * @param {Entity} entity
*/ */
const checker = entity => { const checker = entity => {
if (!this.root.gameInitialized) {
return;
}
// Check for all components // Check for all components
for (let i = 0; i < componentIds.length; ++i) { for (let i = 0; i < componentIds.length; ++i) {
if (entity.components[componentIds[i]]) { if (entity.components[componentIds[i]]) {

@ -2,8 +2,11 @@ import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { Rectangle } from "../../core/rectangle"; import { Rectangle } from "../../core/rectangle";
import { StaleAreaDetector } from "../../core/stale_area_detector";
import { enumDirection, enumDirectionToVector } from "../../core/vector"; import { enumDirection, enumDirectionToVector } from "../../core/vector";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { BeltComponent } from "../components/belt";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
@ -15,103 +18,53 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
super(root, [ItemEjectorComponent]); super(root, [ItemEjectorComponent]);
this.root.signals.entityAdded.add(this.checkForCacheInvalidation, this); this.staleAreaDetector = new StaleAreaDetector({
this.root.signals.entityDestroyed.add(this.checkForCacheInvalidation, this); root: this.root,
this.root.signals.postLoadHook.add(this.recomputeCache, this); name: "item-ejector",
recomputeMethod: this.recomputeArea.bind(this),
});
/** this.staleAreaDetector.recomputeOnComponentsChanged(
* @type {Rectangle} [ItemEjectorComponent, ItemAcceptorComponent, BeltComponent],
*/ 1
this.areaToRecompute = null; );
}
/**
*
* @param {Entity} entity
*/
checkForCacheInvalidation(entity) {
if (!this.root.gameInitialized) {
return;
}
if (!entity.components.StaticMapEntity) {
return;
}
// Optimize for the common case: adding or removing one building at a time. Clicking
// and dragging can cause up to 4 add/remove signals.
const staticComp = entity.components.StaticMapEntity;
const bounds = staticComp.getTileSpaceBounds();
const expandedBounds = bounds.expandedInAllDirections(2);
if (this.areaToRecompute) { this.root.signals.postLoadHook.add(this.recomputeCacheFull, this);
this.areaToRecompute = this.areaToRecompute.getUnion(expandedBounds);
} else {
this.areaToRecompute = expandedBounds;
}
} }
/** /**
* Precomputes the cache, which makes up for a huge performance improvement * Recomputes an area after it changed
* @param {Rectangle} area
*/ */
recomputeCache() { recomputeArea(area) {
if (this.areaToRecompute) { /** @type {Set<number>} */
logger.log("Recomputing cache using rectangle"); const seenUids = new Set();
if (G_IS_DEV && globalConfig.debug.renderChanges) { for (let x = 0; x < area.w; ++x) {
this.root.hud.parts.changesDebugger.renderChange( for (let y = 0; y < area.h; ++y) {
"ejector-area", const tileX = area.x + x;
this.areaToRecompute, const tileY = area.y + y;
"#fe50a6" // @NOTICE: Item ejector currently only supports regular layer
); const contents = this.root.map.getLayerContentXY(tileX, tileY, "regular");
if (contents && contents.components.ItemEjector) {
if (!seenUids.has(contents.uid)) {
seenUids.add(contents.uid);
this.recomputeSingleEntityCache(contents);
} }
this.recomputeAreaCache();
this.areaToRecompute = null;
} else {
logger.log("Full cache recompute");
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.root.hud.parts.changesDebugger.renderChange(
"ejector-full",
new Rectangle(-1000, -1000, 2000, 2000),
"#fe50a6"
);
} }
// Try to find acceptors for every ejector
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
this.recomputeSingleEntityCache(entity);
} }
} }
} }
/** /**
* Recomputes the cache in the given area * Recomputes the whole cache after the game has loaded
*/ */
recomputeAreaCache() { recomputeCacheFull() {
const area = this.areaToRecompute; logger.log("Full cache recompute in post load hook");
let entryCount = 0; for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h);
// Store the entities we already recomputed, so we don't do work twice
const recomputedEntities = new Set();
for (let x = area.x; x < area.right(); ++x) {
for (let y = area.y; y < area.bottom(); ++y) {
const entities = this.root.map.getLayersContentsMultipleXY(x, y);
for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];
// Recompute the entity in case its relevant for this system and it
// hasn't already been computed
if (!recomputedEntities.has(entity.uid) && entity.components.ItemEjector) {
recomputedEntities.add(entity.uid);
this.recomputeSingleEntityCache(entity); this.recomputeSingleEntityCache(entity);
} }
} }
}
}
return entryCount;
}
/** /**
* @param {Entity} entity * @param {Entity} entity
@ -183,9 +136,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
} }
update() { update() {
if (this.areaToRecompute) { this.staleAreaDetector.update();
this.recomputeCache();
}
// Precompute effective belt speed // Precompute effective belt speed
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds; let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;

Loading…
Cancel
Save