Take 3 on the belt performance (+ tslint fixes)

pull/310/head
tobspr 4 years ago
parent 9ce912dbdd
commit a71c0b8039

@ -19,7 +19,7 @@ export class FormElement {
abstract;
}
focus(parent) {}
focus() {}
isValid() {
return true;

@ -72,6 +72,7 @@ function objectPolyfills() {
}
if (!Object.entries) {
// @ts-ignore
Object.entries = function entries(O) {
return reduce(
keys(O),

@ -405,8 +405,6 @@ export function findNiceValue(num) {
return Math_round(niceValue * 100) / 100;
}
window.fn = findNiceValue;
/**
* Finds a nice integer value
* @see findNiceValue

@ -14,6 +14,8 @@ const logger = createLogger("belt_path");
const _nextDistance = 0;
const _item = 1;
const DEBUG = G_IS_DEV && false;
/**
* Stores a path of belts, used for optimizing performance
*/
@ -204,7 +206,7 @@ export class BeltPath {
* @param {Entity} entity
*/
extendOnEnd(entity) {
logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin);
DEBUG && logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin);
const beltComp = entity.components.Belt;
@ -212,7 +214,7 @@ export class BeltPath {
const pendingItem = this.ejectorComp.takeSlotItem(0);
if (pendingItem) {
// Ok, so we have a pending item
logger.log("Taking pending item and putting it back on the path");
DEBUG && logger.log("Taking pending item and putting it back on the path");
this.items.push([0, pendingItem]);
}
@ -222,21 +224,22 @@ export class BeltPath {
// Extend the path length
const additionalLength = beltComp.getEffectiveLengthTiles();
this.totalLength += additionalLength;
logger.log(" Extended total length by", additionalLength, "to", this.totalLength);
DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength);
// If we have no item, just update the distance to the first item
if (this.items.length === 0) {
this.spacingToFirstItem = this.totalLength;
logger.log(" Extended spacing to first to", this.totalLength, "(= total length)");
DEBUG && logger.log(" Extended spacing to first to", this.totalLength, "(= total length)");
} else {
// Otherwise, update the next-distance of the last item
const lastItem = this.items[this.items.length - 1];
logger.log(
" Extended spacing of last item from",
lastItem[_nextDistance],
"to",
lastItem[_nextDistance] + additionalLength
);
DEBUG &&
logger.log(
" Extended spacing of last item from",
lastItem[_nextDistance],
"to",
lastItem[_nextDistance] + additionalLength
);
lastItem[_nextDistance] += additionalLength;
}
@ -257,7 +260,7 @@ export class BeltPath {
extendOnBeginning(entity) {
const beltComp = entity.components.Belt;
logger.log("Extending the path on the beginning");
DEBUG && logger.log("Extending the path on the beginning");
// All items on that belt are simply lost (for now)
@ -277,6 +280,24 @@ export class BeltPath {
this.debug_checkIntegrity("extend-on-begin");
}
/**
* Returns if the given entity is the end entity of the path
* @param {Entity} entity
* @returns {boolean}
*/
isEndEntity(entity) {
return this.entityPath[this.entityPath.length - 1] === entity;
}
/**
* Returns if the given entity is the start entity of the path
* @param {Entity} entity
* @returns {boolean}
*/
isStartEntity(entity) {
return this.entityPath[0] === entity;
}
/**
* Splits this path at the given entity by removing it, and
* returning the new secondary paht
@ -284,7 +305,7 @@ export class BeltPath {
* @returns {BeltPath}
*/
deleteEntityOnPathSplitIntoTwo(entity) {
logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin);
DEBUG && logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin);
// First, find where the current path ends
const beltComp = entity.components.Belt;
@ -302,7 +323,7 @@ export class BeltPath {
for (let i = 0; i < this.entityPath.length; ++i) {
const otherEntity = this.entityPath[i];
if (otherEntity === entity) {
logger.log("Found entity at", i, "of length", firstPathLength);
DEBUG && logger.log("Found entity at", i, "of length", firstPathLength);
break;
}
@ -311,38 +332,41 @@ export class BeltPath {
firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles();
}
logger.log(
"First path ends at",
firstPathLength,
"and entity",
firstPathEndEntity.components.StaticMapEntity.origin,
"and has",
firstPathEntityCount,
"entities"
);
DEBUG &&
logger.log(
"First path ends at",
firstPathLength,
"and entity",
firstPathEndEntity.components.StaticMapEntity.origin,
"and has",
firstPathEntityCount,
"entities"
);
// Compute length of second path
const secondPathLength = this.totalLength - firstPathLength - entityLength;
const secondPathStart = firstPathLength + entityLength;
const secondEntities = this.entityPath.splice(firstPathEntityCount + 1);
logger.log(
"Second path starts at",
secondPathStart,
"and has a length of ",
secondPathLength,
"with",
secondEntities.length,
"entities"
);
DEBUG &&
logger.log(
"Second path starts at",
secondPathStart,
"and has a length of ",
secondPathLength,
"with",
secondEntities.length,
"entities"
);
// Remove the last item
this.entityPath.pop();
logger.log("Splitting", this.items.length, "items");
logger.log(
"Old items are",
this.items.map(i => i[_nextDistance])
);
DEBUG && logger.log("Splitting", this.items.length, "items");
DEBUG &&
logger.log(
"Old items are",
this.items.map(i => i[_nextDistance])
);
// Create second path
const secondPath = new BeltPath(this.root, secondEntities);
@ -353,45 +377,48 @@ export class BeltPath {
const item = this.items[i];
const distanceToNext = item[_nextDistance];
logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next");
DEBUG && logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next");
// Check if this item is past the first path
if (itemPos >= firstPathLength) {
// Remove it from the first path
this.items.splice(i, 1);
i -= 1;
logger.log(" Removed item from first path since its no longer contained @", itemPos);
DEBUG &&
logger.log(" Removed item from first path since its no longer contained @", itemPos);
// Check if its on the second path (otherwise its on the removed belt and simply lost)
if (itemPos >= secondPathStart) {
// Put item on second path
secondPath.items.push([distanceToNext, item[_item]]);
logger.log(
" Put item to second path @",
itemPos,
"with distance to next =",
distanceToNext
);
DEBUG &&
logger.log(
" Put item to second path @",
itemPos,
"with distance to next =",
distanceToNext
);
// If it was the first item, adjust the distance to the first item
if (secondPath.items.length === 1) {
logger.log(" Sinc it was the first, set sapcing of first to", itemPos);
DEBUG && logger.log(" Sinc it was the first, set sapcing of first to", itemPos);
secondPath.spacingToFirstItem = itemPos - secondPathStart;
}
} else {
logger.log(" Item was on the removed belt, so its gone - forever!");
DEBUG && logger.log(" Item was on the removed belt, so its gone - forever!");
}
} else {
// Seems this item is on the first path (so all good), so just make sure it doesn't
// have a nextDistance which is bigger than the total path length
const clampedDistanceToNext = Math_min(itemPos + distanceToNext, firstPathLength) - itemPos;
if (clampedDistanceToNext < distanceToNext) {
logger.log(
"Correcting next distance (first path) from",
distanceToNext,
"to",
clampedDistanceToNext
);
DEBUG &&
logger.log(
"Correcting next distance (first path) from",
distanceToNext,
"to",
clampedDistanceToNext
);
item[_nextDistance] = clampedDistanceToNext;
}
}
@ -400,15 +427,17 @@ export class BeltPath {
itemPos += distanceToNext;
}
logger.log(
"New items are",
this.items.map(i => i[_nextDistance])
);
DEBUG &&
logger.log(
"New items are",
this.items.map(i => i[_nextDistance])
);
logger.log(
"And second path items are",
secondPath.items.map(i => i[_nextDistance])
);
DEBUG &&
logger.log(
"And second path items are",
secondPath.items.map(i => i[_nextDistance])
);
// Adjust our total length
this.totalLength = firstPathLength;
@ -433,25 +462,36 @@ export class BeltPath {
* @param {Entity} entity
*/
deleteEntityOnEnd(entity) {
assert(this.entityPath[this.entityPath.length - 1] === entity, "Not the last entity actually");
assert(
this.entityPath[this.entityPath.length - 1] === entity,
"Not actually the last entity (instead " + this.entityPath.indexOf(entity) + ")"
);
// Ok, first remove the entity
const beltComp = entity.components.Belt;
const beltLength = beltComp.getEffectiveLengthTiles();
logger.log(
"Deleting last entity on path with length",
this.entityPath.length,
"(reducing",
this.totalLength,
" by",
beltLength,
")"
);
DEBUG &&
logger.log(
"Deleting last entity on path with length",
this.entityPath.length,
"(reducing",
this.totalLength,
" by",
beltLength,
")"
);
this.totalLength -= beltLength;
this.entityPath.pop();
logger.log(" New path has length of", this.totalLength, "with", this.entityPath.length, "entities");
DEBUG &&
logger.log(
" New path has length of",
this.totalLength,
"with",
this.entityPath.length,
"entities"
);
// This is just for sanity
beltComp.assignedPath = null;
@ -465,20 +505,20 @@ export class BeltPath {
let itemOffset = this.spacingToFirstItem;
let lastItemOffset = itemOffset;
logger.log(" Adjusting", this.items.length, "items");
DEBUG && logger.log(" Adjusting", this.items.length, "items");
for (let i = 0; i < this.items.length; ++i) {
const item = this.items[i];
// Get rid of items past this path
if (itemOffset >= this.totalLength) {
logger.log("Dropping item (current index=", i, ")");
DEBUG && logger.log("Dropping item (current index=", i, ")");
this.items.splice(i, 1);
i -= 1;
continue;
}
logger.log("Item", i, "is at", itemOffset, "with next offset", item[_nextDistance]);
DEBUG && logger.log("Item", i, "is at", itemOffset, "with next offset", item[_nextDistance]);
lastItemOffset = itemOffset;
itemOffset += item[_nextDistance];
}
@ -497,18 +537,19 @@ export class BeltPath {
lastItemOffset
);
logger.log(
"Adjusted distance of last item: it is at",
lastItemOffset,
"so it has a distance of",
lastDistance,
"to the end (",
this.totalLength,
")"
);
DEBUG &&
logger.log(
"Adjusted distance of last item: it is at",
lastItemOffset,
"so it has a distance of",
lastDistance,
"to the end (",
this.totalLength,
")"
);
this.items[this.items.length - 1][_nextDistance] = lastDistance;
} else {
logger.log(" Removed all items so we'll update spacing to total length");
DEBUG && logger.log(" Removed all items so we'll update spacing to total length");
// We removed all items so update our spacing
this.spacingToFirstItem = this.totalLength;
@ -528,25 +569,36 @@ export class BeltPath {
* @param {Entity} entity
*/
deleteEntityOnStart(entity) {
assert(entity === this.entityPath[0], "Not actually the start entity");
assert(
entity === this.entityPath[0],
"Not actually the start entity (instead " + this.entityPath.indexOf(entity) + ")"
);
// Ok, first remove the entity
const beltComp = entity.components.Belt;
const beltLength = beltComp.getEffectiveLengthTiles();
logger.log(
"Deleting first entity on path with length",
this.entityPath.length,
"(reducing",
this.totalLength,
" by",
beltLength,
")"
);
DEBUG &&
logger.log(
"Deleting first entity on path with length",
this.entityPath.length,
"(reducing",
this.totalLength,
" by",
beltLength,
")"
);
this.totalLength -= beltLength;
this.entityPath.shift();
logger.log(" New path has length of", this.totalLength, "with", this.entityPath.length, "entities");
DEBUG &&
logger.log(
" New path has length of",
this.totalLength,
"with",
this.entityPath.length,
"entities"
);
// This is just for sanity
beltComp.assignedPath = null;
@ -558,41 +610,45 @@ export class BeltPath {
} else {
// Simple case, we had no item on the beginning -> all good
if (this.spacingToFirstItem >= beltLength) {
logger.log(
" No item on the first place, so we can just adjust the spacing (spacing=",
this.spacingToFirstItem,
") removed =",
beltLength
);
DEBUG &&
logger.log(
" No item on the first place, so we can just adjust the spacing (spacing=",
this.spacingToFirstItem,
") removed =",
beltLength
);
this.spacingToFirstItem -= beltLength;
} else {
// Welp, okay we need to drop all items which are < beltLength and adjust
// the other item offsets as well
logger.log(
" We have at least one item in the beginning, drop those and adjust spacing (first item @",
this.spacingToFirstItem,
") since we removed",
beltLength,
"length from path"
);
logger.log(
" Items:",
this.items.map(i => i[_nextDistance])
);
DEBUG &&
logger.log(
" We have at least one item in the beginning, drop those and adjust spacing (first item @",
this.spacingToFirstItem,
") since we removed",
beltLength,
"length from path"
);
DEBUG &&
logger.log(
" Items:",
this.items.map(i => i[_nextDistance])
);
// Find offset to first item
let itemOffset = this.spacingToFirstItem;
for (let i = 0; i < this.items.length; ++i) {
const item = this.items[i];
if (itemOffset <= beltLength) {
logger.log(
" -> Dropping item with index",
i,
"at",
itemOffset,
"since it was on the removed belt"
);
DEBUG &&
logger.log(
" -> Dropping item with index",
i,
"at",
itemOffset,
"since it was on the removed belt"
);
// This item must be dropped
this.items.splice(i, 1);
i -= 1;
@ -605,13 +661,14 @@ export class BeltPath {
}
if (this.items.length > 0) {
logger.log(
" Offset of first non-dropped item was at:",
itemOffset,
"-> setting spacing to it (total length=",
this.totalLength,
")"
);
DEBUG &&
logger.log(
" Offset of first non-dropped item was at:",
itemOffset,
"-> setting spacing to it (total length=",
this.totalLength,
")"
);
this.spacingToFirstItem = itemOffset - beltLength;
assert(
@ -619,7 +676,7 @@ export class BeltPath {
"Invalid spacing after delete on start: " + this.spacingToFirstItem
);
} else {
logger.log(" We dropped all items, simply set spacing to total length");
DEBUG && logger.log(" We dropped all items, simply set spacing to total length");
// We dropped all items, simple one
this.spacingToFirstItem = this.totalLength;
}
@ -640,11 +697,11 @@ export class BeltPath {
assert(otherPath !== this, "Circular path dependency");
const entities = otherPath.entityPath;
logger.log("Extending path by other path, starting to add entities");
DEBUG && logger.log("Extending path by other path, starting to add entities");
const oldLength = this.totalLength;
logger.log(" Adding", entities.length, "new entities, current length =", this.totalLength);
DEBUG && logger.log(" Adding", entities.length, "new entities, current length =", this.totalLength);
// First, append entities
for (let i = 0; i < entities.length; ++i) {
@ -660,7 +717,13 @@ export class BeltPath {
this.totalLength += additionalLength;
}
logger.log(" Path is now", this.entityPath.length, "entities and has a length of", this.totalLength);
DEBUG &&
logger.log(
" Path is now",
this.entityPath.length,
"entities and has a length of",
this.totalLength
);
// Update handles
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
@ -670,21 +733,23 @@ export class BeltPath {
if (this.items.length !== 0) {
const lastItem = this.items[this.items.length - 1];
lastItem[_nextDistance] += otherPath.spacingToFirstItem;
logger.log(" Add distance to last item, effectively being", lastItem[_nextDistance], "now");
DEBUG &&
logger.log(" Add distance to last item, effectively being", lastItem[_nextDistance], "now");
} else {
// Seems we have no items, update our first item distance
this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem;
logger.log(
" We had no items, so our new spacing to first is old length (",
oldLength,
") plus others spacing to first (",
otherPath.spacingToFirstItem,
") =",
this.spacingToFirstItem
);
DEBUG &&
logger.log(
" We had no items, so our new spacing to first is old length (",
oldLength,
") plus others spacing to first (",
otherPath.spacingToFirstItem,
") =",
this.spacingToFirstItem
);
}
logger.log(" Pushing", otherPath.items.length, "items from other path");
DEBUG && logger.log(" Pushing", otherPath.items.length, "items from other path");
// Aaand push the other paths items
for (let i = 0; i < otherPath.items.length; ++i) {

@ -1,13 +1,13 @@
import { DrawParameters } from "../../../core/draw_parameters";
import { Loader } from "../../../core/loader";
import { createLogger } from "../../../core/logging";
import { Vector } from "../../../core/vector";
import { Entity } from "../../entity";
import { GameRoot } from "../../root";
import { findNiceIntegerValue } from "../../../core/utils";
import { Math_pow } from "../../../core/builtins";
import { blueprintShape } from "../../upgrades";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../core/draw_parameters";
import { Loader } from "../core/loader";
import { createLogger } from "../core/logging";
import { Vector } from "../core/vector";
import { Entity } from "./entity";
import { GameRoot } from "./root";
import { findNiceIntegerValue } from "../core/utils";
import { Math_pow } from "../core/builtins";
import { blueprintShape } from "./upgrades";
import { globalConfig } from "../core/config";
const logger = createLogger("blueprint");
@ -176,7 +176,6 @@ export class Blueprint {
tryPlace(root, tile) {
return root.logic.performBulkOperation(() => {
let anyPlaced = false;
const beltsToRegisterLater = [];
for (let i = 0; i < this.entities.length; ++i) {
let placeable = true;
const entity = this.entities[i];
@ -217,21 +216,10 @@ export class Blueprint {
root.map.placeStaticEntity(clone);
// Registering a belt immediately triggers a recalculation of surrounding belt
// directions, which is no good when not all belts have been placed. To resolve
// this, only register belts after all entities have been placed.
if (!clone.components.Belt) {
root.entityMgr.registerEntity(clone);
} else {
beltsToRegisterLater.push(clone);
}
root.entityMgr.registerEntity(clone);
anyPlaced = true;
}
}
for (let i = 0; i < beltsToRegisterLater.length; i++) {
root.entityMgr.registerEntity(beltsToRegisterLater[i]);
}
return anyPlaced;
});
}

@ -9,7 +9,7 @@ import { KEYMAPPINGS } from "../../key_action_mapper";
import { blueprintShape } from "../../upgrades";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { Blueprint } from "./blueprint";
import { Blueprint } from "../../blueprint";
import { SOUNDS } from "../../../platform/sound";
export class HUDBlueprintPlacer extends BaseHUDPart {

@ -196,7 +196,7 @@ export class GameLogic {
* @param {function} operation
*/
performBulkOperation(operation) {
logger.log("Running bulk operation ...");
logger.warn("Running bulk operation ...");
assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice");
this.root.bulkOperationRunning = true;
const now = performanceNow();

@ -146,38 +146,35 @@ export class BeltSystem extends GameSystemWithFilter {
const assignedPath = entity.components.Belt.assignedPath;
assert(assignedPath, "Entity has no belt path assigned");
this.deleteEntityFromPath(assignedPath, entity);
this.verifyBeltPaths();
}
// Find from and to entities
const fromEntity = this.findSupplyingEntity(entity);
const toEntity = this.findFollowUpEntity(entity);
// Check if the belt had a previous belt
if (fromEntity) {
const fromPath = fromEntity.components.Belt.assignedPath;
// Check if the entity had a followup - belt
if (toEntity) {
const toPath = toEntity.components.Belt.assignedPath;
assert(fromPath === toPath, "Invalid belt path layout (from path != to path)");
/**
* Attempts to delete the belt from its current path
* @param {BeltPath} path
* @param {Entity} entity
*/
deleteEntityFromPath(path, entity) {
if (path.entityPath.length === 1) {
// This is a single entity path, easy to do, simply erase whole path
fastArrayDeleteValue(this.beltPaths, path);
return;
}
const newPath = fromPath.deleteEntityOnPathSplitIntoTwo(entity);
this.beltPaths.push(newPath);
} else {
fromPath.deleteEntityOnEnd(entity);
}
// Notice: Since there might be circular references, it is important to check
// which role the entity has
if (path.isStartEntity(entity)) {
// We tried to delete the start
path.deleteEntityOnStart(entity);
} else if (path.isEndEntity(entity)) {
// We tried to delete the end
path.deleteEntityOnEnd(entity);
} else {
if (toEntity) {
// We need to remove the entity from the beginning of the other path
const toPath = toEntity.components.Belt.assignedPath;
toPath.deleteEntityOnStart(entity);
} else {
// This is a single entity path, easy to do
const path = entity.components.Belt.assignedPath;
fastArrayDeleteValue(this.beltPaths, path);
}
// We tried to delete something inbetween
const newPath = path.deleteEntityOnPathSplitIntoTwo(entity);
this.beltPaths.push(newPath);
}
this.verifyBeltPaths();
}
/**
@ -193,13 +190,9 @@ export class BeltSystem extends GameSystemWithFilter {
return;
}
console.log("ADD");
const fromEntity = this.findSupplyingEntity(entity);
const toEntity = this.findFollowUpEntity(entity);
console.log("From:", fromEntity, "to:", toEntity);
// Check if we can add the entity to the previous path
if (fromEntity) {
const fromPath = fromEntity.components.Belt.assignedPath;
@ -385,22 +378,15 @@ export class BeltSystem extends GameSystemWithFilter {
*/
computeBeltPaths() {
const visitedUids = new Set();
console.log("Computing belt paths");
const debugEntity = e => e.components.StaticMapEntity.origin.toString();
// const stackToVisit = this.allEntities.slice();
const result = [];
const currentPath = null;
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
if (visitedUids.has(entity.uid)) {
continue;
}
// console.log("Starting at", debugEntity(entity));
// Mark entity as visited
visitedUids.add(entity.uid);
@ -412,10 +398,9 @@ export class BeltSystem extends GameSystemWithFilter {
// Find precedors
let prevEntity = this.findSupplyingEntity(entity);
while (prevEntity && --maxIter > 0) {
if (visitedUids.has(prevEntity)) {
if (visitedUids.has(prevEntity.uid)) {
break;
}
// console.log(" -> precedor: ", debugEntity(prevEntity));
path.unshift(prevEntity);
visitedUids.add(prevEntity.uid);
prevEntity = this.findSupplyingEntity(prevEntity);
@ -424,26 +409,17 @@ export class BeltSystem extends GameSystemWithFilter {
// Find succedors
let nextEntity = this.findFollowUpEntity(entity);
while (nextEntity && --maxIter > 0) {
if (visitedUids.has(nextEntity)) {
if (visitedUids.has(nextEntity.uid)) {
break;
}
// console.log(" -> succedor: ", debugEntity(nextEntity));
path.push(nextEntity);
visitedUids.add(nextEntity.uid);
nextEntity = this.findFollowUpEntity(nextEntity);
}
assert(maxIter !== 0, "Ran out of iterations");
// console.log(
// "Found path:",
// path.map(e => debugEntity(e))
// );
assert(maxIter > 1, "Ran out of iterations");
result.push(new BeltPath(this.root, path));
// let prevEntity = this.findSupplyingEntity(srcEntity);
}
logger.log("Found", this.beltPaths.length, "belt paths");

Loading…
Cancel
Save