first commmit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Generic object pool used by damage effects, bullets, enemies and VFX
|
||||
* (requirement 18.5). Pure-TS, platform-agnostic, Jest-testable.
|
||||
*
|
||||
* Design notes:
|
||||
* - `factory` creates a brand-new instance when the free list is empty.
|
||||
* - `resetter` is invoked on every released object, letting the caller wipe
|
||||
* transient state (position, timers, listeners) before it goes back to
|
||||
* the pool.
|
||||
* - `maxSize` caps the retained instances; objects released beyond the cap
|
||||
* are dropped (letting the GC collect them) to bound memory usage
|
||||
* (requirement 18.4: memory peak ≤ 200MB).
|
||||
* - Double-release is silently ignored but reported through `onDoubleRelease`
|
||||
* so tests / Logger can assert correctness.
|
||||
*/
|
||||
|
||||
export type ObjectFactory<T> = () => T;
|
||||
export type ObjectResetter<T> = (obj: T) => void;
|
||||
|
||||
export interface ObjectPoolOptions<T> {
|
||||
/** Required creator invoked when the pool is empty. */
|
||||
factory: ObjectFactory<T>;
|
||||
/** Optional cleaner invoked on every `release`. */
|
||||
resetter?: ObjectResetter<T>;
|
||||
/** Max retained objects; excess releases are discarded. Default 128. */
|
||||
maxSize?: number;
|
||||
/** Optional pre-warm count (creates this many objects upfront). Default 0. */
|
||||
preAlloc?: number;
|
||||
/** Optional diagnostic hook. */
|
||||
onDoubleRelease?: (obj: T) => void;
|
||||
}
|
||||
|
||||
export class ObjectPool<T> {
|
||||
private readonly free: T[] = [];
|
||||
private readonly borrowed = new Set<T>();
|
||||
private readonly factory: ObjectFactory<T>;
|
||||
private readonly resetter?: ObjectResetter<T>;
|
||||
private readonly maxSize: number;
|
||||
private readonly onDoubleRelease?: (obj: T) => void;
|
||||
|
||||
// Diagnostics
|
||||
private _acquiredTotal = 0;
|
||||
private _recycledTotal = 0;
|
||||
private _createdTotal = 0;
|
||||
|
||||
constructor(options: ObjectPoolOptions<T>) {
|
||||
this.factory = options.factory;
|
||||
this.resetter = options.resetter;
|
||||
this.maxSize = options.maxSize ?? 128;
|
||||
this.onDoubleRelease = options.onDoubleRelease;
|
||||
|
||||
const preAlloc = options.preAlloc ?? 0;
|
||||
for (let i = 0; i < preAlloc; i++) {
|
||||
const inst = this.factory();
|
||||
this._createdTotal++;
|
||||
this.free.push(inst);
|
||||
}
|
||||
}
|
||||
|
||||
/** Acquire an object from the pool (creates one if empty). */
|
||||
public acquire(): T {
|
||||
this._acquiredTotal++;
|
||||
const inst = this.free.pop();
|
||||
if (inst !== undefined) {
|
||||
this.borrowed.add(inst);
|
||||
return inst;
|
||||
}
|
||||
const created = this.factory();
|
||||
this._createdTotal++;
|
||||
this.borrowed.add(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
/** Release an object back to the pool. Double-releases are ignored. */
|
||||
public release(obj: T): void {
|
||||
if (!this.borrowed.has(obj)) {
|
||||
this.onDoubleRelease?.(obj);
|
||||
return;
|
||||
}
|
||||
this.borrowed.delete(obj);
|
||||
this.resetter?.(obj);
|
||||
if (this.free.length < this.maxSize) {
|
||||
this.free.push(obj);
|
||||
this._recycledTotal++;
|
||||
}
|
||||
// else: drop the object so the GC can reclaim it.
|
||||
}
|
||||
|
||||
/** Number of objects currently held in the free list. */
|
||||
public get freeCount(): number {
|
||||
return this.free.length;
|
||||
}
|
||||
|
||||
/** Number of objects that are currently out on loan. */
|
||||
public get borrowedCount(): number {
|
||||
return this.borrowed.size;
|
||||
}
|
||||
|
||||
/** Drop everything. Used on scene unload. */
|
||||
public drain(): void {
|
||||
this.free.length = 0;
|
||||
this.borrowed.clear();
|
||||
}
|
||||
|
||||
/** Diagnostic stats (used by Logger / perf BI埋点). */
|
||||
public stats() {
|
||||
return {
|
||||
free: this.freeCount,
|
||||
borrowed: this.borrowedCount,
|
||||
acquired: this._acquiredTotal,
|
||||
recycled: this._recycledTotal,
|
||||
created: this._createdTotal,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user