1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2024-10-27 20:34:29 +00:00

Remove belt cache array; use BeltComponent instead

Removed the belt cache array. Follow-up belts are cached in the belt's
BeltComponent instead. This change also removes the recursive follow-up
search, which could cause a stack overflow for an extremely long belt
chain.

Saves one object allocation per belt per change, two very large array
allocations per change, many function calls, and belts are only visited
exactly once per change.
This commit is contained in:
Phlosioneer 2020-06-16 19:43:29 -04:00
parent b753187cde
commit 8a50fdb392
2 changed files with 13 additions and 41 deletions

View File

@ -5,6 +5,7 @@ import { BaseItem } from "../base_item";
import { Vector, enumDirection } from "../../core/vector"; import { Vector, enumDirection } from "../../core/vector";
import { Math_PI, Math_sin, Math_cos } from "../../core/builtins"; import { Math_PI, Math_sin, Math_cos } from "../../core/builtins";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { Entity } from "../entity";
export class BeltComponent extends Component { export class BeltComponent extends Component {
static getId() { static getId() {
@ -12,6 +13,7 @@ export class BeltComponent extends Component {
} }
static getSchema() { static getSchema() {
// The followUpCache field is not serialized.
return { return {
direction: types.string, direction: types.string,
sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))), sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))),
@ -34,6 +36,9 @@ export class BeltComponent extends Component {
/** @type {Array<[number, BaseItem]>} */ /** @type {Array<[number, BaseItem]>} */
this.sortedItems = []; this.sortedItems = [];
/** @type {Entity} */
this.followUpCache = null;
} }
/** /**

View File

@ -19,8 +19,6 @@ const SQRT_2 = Math_sqrt(2);
const logger = createLogger("belt"); const logger = createLogger("belt");
/** @typedef {Array<{ entity: Entity, followUp: Entity }>} BeltCache */
export class BeltSystem extends GameSystemWithFilter { export class BeltSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
super(root, [BeltComponent]); super(root, [BeltComponent]);
@ -66,9 +64,6 @@ export class BeltSystem extends GameSystemWithFilter {
this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);
this.cacheNeedsUpdate = true; this.cacheNeedsUpdate = true;
/** @type {BeltCache} */
this.beltCache = [];
} }
/** /**
@ -163,42 +158,14 @@ export class BeltSystem extends GameSystemWithFilter {
return null; return null;
} }
/**
* Adds a single entity to the cache
* @param {Entity} entity
* @param {BeltCache} cache
* @param {Set} visited
*/
computeSingleBeltCache(entity, cache, visited) {
// Check for double visit
if (visited.has(entity.uid)) {
return;
}
visited.add(entity.uid);
const followUp = this.findFollowUpEntity(entity);
if (followUp) {
// Process followup first
this.computeSingleBeltCache(followUp, cache, visited);
}
cache.push({ entity, followUp });
}
computeBeltCache() { computeBeltCache() {
logger.log("Updating belt cache"); logger.log("Updating belt cache");
let cache = [];
let visited = new Set(); let visited = new Set();
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
this.computeSingleBeltCache(this.allEntities[i], cache, visited); const entity = this.allEntities[i];
entity.components.Belt.followUpCache = this.findFollowUpEntity(entity);
} }
assert(
cache.length === this.allEntities.length,
"Belt cache mismatch: Has " + cache.length + " entries but should have " + this.allEntities.length
);
this.beltCache = cache;
} }
update() { update() {
@ -217,8 +184,8 @@ export class BeltSystem extends GameSystemWithFilter {
beltSpeed *= 100; beltSpeed *= 100;
} }
for (let i = 0; i < this.beltCache.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const { entity, followUp } = this.beltCache[i]; const entity = this.allEntities[i];
const beltComp = entity.components.Belt; const beltComp = entity.components.Belt;
const items = beltComp.sortedItems; const items = beltComp.sortedItems;
@ -244,8 +211,8 @@ export class BeltSystem extends GameSystemWithFilter {
maxProgress = 1 - globalConfig.itemSpacingOnBelts; maxProgress = 1 - globalConfig.itemSpacingOnBelts;
} else { } else {
// Otherwise our progress depends on the follow up // Otherwise our progress depends on the follow up
if (followUp) { if (beltComp.followUpCache) {
const spacingOnBelt = followUp.components.Belt.getDistanceToFirstItemCenter(); const spacingOnBelt = beltComp.followUpCache.components.Belt.getDistanceToFirstItemCenter();
maxProgress = Math.min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt); maxProgress = Math.min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt);
// Useful check, but hurts performance // Useful check, but hurts performance
@ -270,8 +237,8 @@ export class BeltSystem extends GameSystemWithFilter {
progressAndItem[0] = Math.min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed); progressAndItem[0] = Math.min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed);
if (progressAndItem[0] >= 1.0) { if (progressAndItem[0] >= 1.0) {
if (followUp) { if (beltComp.followUpCache) {
const followUpBelt = followUp.components.Belt; const followUpBelt = beltComp.followUpCache.components.Belt;
if (followUpBelt.canAcceptItem()) { if (followUpBelt.canAcceptItem()) {
followUpBelt.takeItem(progressAndItem[1], progressAndItem[0] - 1.0); followUpBelt.takeItem(progressAndItem[1], progressAndItem[0] - 1.0);
items.splice(itemIndex, 1); items.splice(itemIndex, 1);