From 746f4935ad7d507884b5682b156805cefd32acac Mon Sep 17 00:00:00 2001 From: tobspr Date: Fri, 18 Sep 2020 18:32:53 +0200 Subject: [PATCH] Improve item ejector system --- src/js/core/stale_area_detector.js | 8 +- src/js/game/systems/item_ejector.js | 123 +++++++++------------------- 2 files changed, 43 insertions(+), 88 deletions(-) diff --git a/src/js/core/stale_area_detector.js b/src/js/core/stale_area_detector.js index 06415fd2..5048ee37 100644 --- a/src/js/core/stale_area_detector.js +++ b/src/js/core/stale_area_detector.js @@ -40,9 +40,9 @@ export class StaleAreaDetector { * Makes this detector recompute the area of an entity whenever * it changes in any way * @param {Array} 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()); /** @@ -50,6 +50,10 @@ export class StaleAreaDetector { * @param {Entity} entity */ const checker = entity => { + if (!this.root.gameInitialized) { + return; + } + // Check for all components for (let i = 0; i < componentIds.length; ++i) { if (entity.components[componentIds[i]]) { diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 8fe1cbc5..c5041a2e 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -2,8 +2,11 @@ import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; import { createLogger } from "../../core/logging"; import { Rectangle } from "../../core/rectangle"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; import { enumDirection, enumDirectionToVector } from "../../core/vector"; import { BaseItem } from "../base_item"; +import { BeltComponent } from "../components/belt"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; @@ -15,102 +18,52 @@ export class ItemEjectorSystem extends GameSystemWithFilter { constructor(root) { super(root, [ItemEjectorComponent]); - this.root.signals.entityAdded.add(this.checkForCacheInvalidation, this); - this.root.signals.entityDestroyed.add(this.checkForCacheInvalidation, this); - this.root.signals.postLoadHook.add(this.recomputeCache, this); + this.staleAreaDetector = new StaleAreaDetector({ + root: this.root, + name: "item-ejector", + recomputeMethod: this.recomputeArea.bind(this), + }); - /** - * @type {Rectangle} - */ - 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); + this.staleAreaDetector.recomputeOnComponentsChanged( + [ItemEjectorComponent, ItemAcceptorComponent, BeltComponent], + 1 + ); - if (this.areaToRecompute) { - this.areaToRecompute = this.areaToRecompute.getUnion(expandedBounds); - } else { - this.areaToRecompute = expandedBounds; - } + this.root.signals.postLoadHook.add(this.recomputeCacheFull, this); } /** - * Precomputes the cache, which makes up for a huge performance improvement + * Recomputes an area after it changed + * @param {Rectangle} area */ - recomputeCache() { - if (this.areaToRecompute) { - logger.log("Recomputing cache using rectangle"); - if (G_IS_DEV && globalConfig.debug.renderChanges) { - this.root.hud.parts.changesDebugger.renderChange( - "ejector-area", - this.areaToRecompute, - "#fe50a6" - ); - } - 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); + recomputeArea(area) { + /** @type {Set} */ + const seenUids = new Set(); + for (let x = 0; x < area.w; ++x) { + for (let y = 0; y < area.h; ++y) { + const tileX = area.x + x; + const tileY = area.y + y; + // @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); + } + } } } } /** - * Recomputes the cache in the given area + * Recomputes the whole cache after the game has loaded */ - recomputeAreaCache() { - const area = this.areaToRecompute; - let entryCount = 0; - - 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); - } - } - } + recomputeCacheFull() { + logger.log("Full cache recompute in post load hook"); + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + this.recomputeSingleEntityCache(entity); } - return entryCount; } /** @@ -183,9 +136,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } update() { - if (this.areaToRecompute) { - this.recomputeCache(); - } + this.staleAreaDetector.update(); // Precompute effective belt speed let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;