Skip to content

Commit

Permalink
Clean up EntityPool and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
corbanbrook committed Mar 29, 2024
1 parent 3988245 commit 1e0181d
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 38 deletions.
63 changes: 42 additions & 21 deletions src/EntityPool.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,67 @@
import { ComponentOf, ComponentTypes } from './Component'
import { Entity } from './Entity'

/**
* Manages a pool of reusable Entities.
* Once an Entity is released, it can be renewed with new components
*/
export class EntityPool<C extends ComponentTypes> {
head: number = -1
entities: Entity<C>[]
readonly entities: Entity<C>[]

constructor(public size: number) {
/** Size of the pool */
readonly size: number = 0

/** Cursor to the next available entity in the pool */
private head: number = -1

constructor(size: number) {
this.size = size
this.entities = new Array(size)

// Fill the pool with new entities
for (let idx = 0; idx < size; idx++) {
this.entities[++this.head] = new Entity<C>()
}
}

/**
* The count of used entities in the pool
* */
get length(): number {
return this.size - this.head - 1
}

// Take an Entity from the pool
renew(components: ComponentOf<C>[] = []): Entity<C> {
if (this.head >= 0) {
const entity = this.entities[this.head--]
return entity.renew(components)
} else {
/**
* Whether the pool is full or not
* */
get isExhausted(): boolean {
return this.head === -1
}

/**
* Get an Entity from the pool
* */
getEntity(components: ComponentOf<C>[] = []): Entity<C> {
if (this.isExhausted) {
throw new Error(
'EntityPool: Attempted to take an Entity from an exhausted pool.'
)
}

const entity = this.entities[this.head--]
return entity.renew(components)
}

// Release an Entity back into the pool
release(entity: Entity<C>) {
if (entity instanceof Entity) {
if (this.head < this.size - 1) {
this.entities[++this.head] = entity
} else {
throw new Error(
'EntityPool: Attempted to release an Entity back into a full pool.'
)
}
} else {
throw new Error('EntityPool: Released object was not an Entity.')
/**
* Release an Entity back into the pool
* */
releaseEntity(entity: Entity<C>) {
if (this.head === this.size - 1) {
throw new Error(
'EntityPool: Attempted to release an Entity back into a full pool.'
)
}

this.entities[++this.head] = entity
}
}
52 changes: 35 additions & 17 deletions tests/EntityPool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EntityPool } from '../src/EntityPool'

const SIZE = 3

describe('EntityPool', () => {
describe.only('EntityPool', () => {
test('can create pool', () => {
const pool = new EntityPool(SIZE)

Expand All @@ -16,47 +16,65 @@ describe('EntityPool', () => {
expect(pool.entities[SIZE - 1]).toBeInstanceOf(Entity)
})

test('can renew an entity', () => {
test('can get an entity', () => {
const pool = new EntityPool(SIZE)

expect(pool.length).toBe(0)

const entity = pool.renew()
const entity = pool.getEntity()

expect(pool.length).toBe(1)
expect(entity).toBeInstanceOf(Entity)
})

test('can release an entity back into the pool', () => {
test.only('can release an entity back into the pool', () => {
const pool = new EntityPool(SIZE)

expect(pool.length).toBe(0)
expect(pool.length, 'starting pool size to be empty').toBe(0)

const entity = pool.renew()
const id = entity.id
// Get an entity from the pool
const entity1 = pool.getEntity()

expect(pool.length).toBe(1)
expect(entity).toBeInstanceOf(Entity)
expect(pool.length, 'pool size to increase after getting an entity').toBe(1)
expect(entity1).toBeInstanceOf(Entity)

pool.release(entity)
// Release the entity back into the pool
pool.releaseEntity(entity1)

expect(entity.id).toBe(id + 1)
expect(pool.length).toBe(0)
expect(
pool.length,
'the pool length to be zero after releasing entity'
).toBe(0)

const another = pool.renew()
// Get another entity from the pool
const entity2 = pool.getEntity()
expect(
entity2,
'the next entity to share the same reference as the released entity'
).toBe(entity1)

expect(another.id).toBe(id + 1)
// Get another entity from the pool
const entity3 = pool.getEntity()
expect(entity3, 'the next entity to be a different entity').not.toBe(
entity1
)
})

test('exhausting the pool throws an error', () => {
const pool = new EntityPool(SIZE)

expect(pool.length).toBe(0)

// Take all entities from pool
for (let i = 0; i < SIZE; i++) {
pool.renew()
pool.getEntity()
}

// Take one more
expect(() => pool.renew()).toThrow()
expect(pool.length).toBe(SIZE)

expect(pool.isExhausted).toBe(true)

// // Take one more
// expect(() => pool.getEntity()).toThrow()
})
})

0 comments on commit 1e0181d

Please sign in to comment.