@game-vir/entity
Version:
Entity system for game development.
72 lines (71 loc) • 2.54 kB
JavaScript
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
import { getOrSetFromMap, makeWritable } from '@augment-vir/common';
/**
* Map all ancestor constructors of an object to the objects.
*
* @category Internal
*/
export class ConstructorMap {
topMostConstructor;
/** A map of constructors to their added instances. */
map = new Map();
isDestroyed = false;
constructor(topMostConstructor = undefined) {
this.topMostConstructor = topMostConstructor;
}
/**
* Add a new instance, mapping each of its ancestor constructors to it inside
* {@link ConstructorMap.map}.
*/
add(instance) {
if (this.isDestroyed) {
throw new Error('Cannot operate on destroyed ConstructorMap.');
}
this.traverseConstructors(instance, Object.getPrototypeOf(instance), 'add');
}
/** Gets all added instances of the given constructor. */
getInstances(constructor) {
if (this.isDestroyed) {
throw new Error('Cannot operate on destroyed ConstructorMap.');
}
return getOrSetFromMap(this.map, constructor, () => new Set());
}
/** Remove a new instance, removing it from all mappings inside {@link ConstructorMap.map}. */
remove(instance) {
if (this.isDestroyed) {
throw new Error('Cannot operate on destroyed ConstructorMap.');
}
this.traverseConstructors(instance, Object.getPrototypeOf(instance), 'remove');
}
/** Recursively map all ancestor prototypes to the given instance. */
traverseConstructors(instance, prototype, operation) {
const constructor = prototype.constructor;
if (!constructor ||
constructor === Function ||
constructor === Object ||
constructor === this.topMostConstructor) {
/** Stop recursing into constructors. */
return;
}
if (operation === 'add') {
const set = getOrSetFromMap(this.map, constructor, () => new Set());
set.add(instance);
}
else {
const set = this.map.get(constructor);
if (set) {
set.delete(instance);
}
}
this.traverseConstructors(instance, Object.getPrototypeOf(prototype), operation);
}
/** Clean up the internal map. */
destroy() {
if (this.isDestroyed) {
return;
}
makeWritable(this).isDestroyed = true;
this.map.clear();
delete this.map;
}
}