@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
183 lines (154 loc) • 4.5 kB
JavaScript
import { assert } from "../../core/assert.js";
/**
* Uniquely identifies an entity by both its ID and generation.
* Lets us uniquely distinguish between two entities, even those with the same ID that were created at different times
* @example
* // 1. Get your EntityComponentDataset to where you manager entities.
* const ecd = ... ; // EntityComponentDataset
*
* // 2. Create an entity (we'll get an ID).
* const entityId = ecd.createEntity();
*
* // 3. Create an EntityReference and bind it to the entity.
* const entityRef = EntityReference.bind(ecd, entityId);
*
* // 4. After you no longer need the entity - destroy the entity via reference.
* entityRef.destroy(ecd);
*
* @see {@link EntityComponentDataset}
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class EntityReference {
/**
* Entity ID
* When entity is live - this is the entity ID inside associated `EntityComponentDataset`, when the entity is not live - it's set to -1
* @type {number}
*/
id = -1
/**
* Entity generation number. This uniquely identifies an entity in combination with the ID
* Generation of an existing entity must match for it to be considered "the same".
* @see {@link EntityComponentDataset.getEntityGeneration}
* @type {number}
*/
generation = -1
/**
*
* @param {EntityReference} other
*/
copy(other) {
assert.defined(other, 'other');
this.id = other.id;
this.generation = other.generation;
}
/**
*
* @returns {EntityReference}
*/
clone() {
const r = new EntityReference();
r.copy(this);
return r;
}
/**
*
* @return {number}
*/
hash() {
return (this.id * 31) ^ this.generation;
}
/**
*
* @param {EntityReference} other
* @return {boolean}
*/
equals(other) {
return this.id === other.id
&& this.generation === other.generation
;
}
/**
* Checks whether referenced entity exists and the generation matches
* @param {EntityComponentDataset} ecd
* @returns {boolean}
*/
verify(ecd) {
if (this.id < 0) {
// special case
return false;
}
return ecd.entityExists(this.id)
&& this.generation === ecd.getEntityGeneration(this.id)
;
}
/**
* Destroys entity bound to this reference
* If the reference is invalid for the given dataset - does nothing
* @param {EntityComponentDataset} ecd
* @returns {boolean} true if entity was destroyed, false otherwise
*/
destroy(ecd) {
if (this.verify(ecd)) {
ecd.removeEntity(this.id);
return true;
} else {
return false;
}
}
/**
* Bind reference to a specific entity
* @param {EntityComponentDataset} ecd
* @param {number} entity
*/
bind(ecd, entity) {
assert.defined(ecd, 'ecd');
assert.equal(ecd.isEntityComponentDataset, true, 'ecd.isEntityComponentDataset !== true');
const generation = ecd.getEntityGeneration(entity);
this.from(entity, generation);
}
/**
*
* @param {number} id
* @param {number} generation
*/
from(id, generation) {
assert.isNonNegativeInteger(id, 'id');
assert.isNonNegativeInteger(generation, 'generation');
this.id = id;
this.generation = generation;
}
/**
*
* @param {number} id
* @param {number} generation
* @return {EntityReference}
*/
static from(id, generation) {
const r = new EntityReference();
r.from(id, generation);
return r;
}
/**
*
* @param {EntityComponentDataset} ecd
* @param {number} id
* @returns {EntityReference}
*/
static bind(ecd, id) {
const r = new EntityReference();
r.bind(ecd, id);
return r;
}
}
/**
* @readonly
* @type {boolean}
*/
EntityReference.prototype.isEntityReference = true;
/**
* Special utility singleton to describe an invalid reference.
* @readonly
* @type {EntityReference}
*/
EntityReference.NULL = Object.freeze(new EntityReference());