You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

85 lines
2.0 KiB

type EventCallback = (...args: unknown[]) => void;
type AnyEventCallback = (event: string, ...args: unknown[]) => void;
export interface RuntimeEventMap {
"runtime:ready": { startedAt: number };
"scene:changed": { current: string; previous?: string };
"game:rendered": undefined;
"game:frame-rendered": undefined;
}
export class RuntimeEvents {
private events = new Map<string, Set<EventCallback>>();
private anyEventCallbacks = new Set<AnyEventCallback>();
on(event: string, callback: EventCallback): () => void {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
this.events.get(event)!.add(callback);
return () => this.off(event, callback);
}
off(event: string, callback: EventCallback): void {
const callbacks = this.events.get(event);
if (!callbacks) {
return;
}
callbacks.delete(callback);
if (callbacks.size === 0) {
this.events.delete(event);
}
}
once(event: string, callback: EventCallback): () => void {
const wrapped: EventCallback = (...args) => {
try {
callback(...args);
} finally {
this.off(event, wrapped);
}
};
return this.on(event, wrapped);
}
emit(event: string, ...args: unknown[]): void {
this.anyEventCallbacks.forEach((cb) => {
try {
cb(event, ...args);
} catch (error) {
console.error(`RuntimeEvents: tap listener failed for "${event}"`, error);
}
});
const callbacks = this.events.get(event);
if (!callbacks) {
return;
}
callbacks.forEach((cb) => {
try {
cb(...args);
} catch (error) {
console.error(`RuntimeEvents: listener failed for "${event}"`, error);
}
});
}
clear(): void {
this.events.clear();
this.anyEventCallbacks.clear();
}
getEventCount(): number {
return this.events.size;
}
onAny(callback: AnyEventCallback): () => void {
this.anyEventCallbacks.add(callback);
return () => {
this.anyEventCallbacks.delete(callback);
};
}
}