Skip to content

Commit 4c5b470

Browse files
committed
use event system
1 parent 4536b85 commit 4c5b470

9 files changed

Lines changed: 136 additions & 122 deletions

File tree

src/core/hooks/useComponentRegistry.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface IComponentRegistryHook {
2222

2323
// Entity queries
2424
listEntityComponents: (entityId: EntityId) => string[];
25+
getEntitiesWithComponent: (componentId: string) => EntityId[];
26+
removeComponentsForEntity: (entityId: EntityId) => void;
2527

2628
// Registry queries
2729
listComponents: () => string[];
@@ -103,13 +105,23 @@ export function useComponentRegistry(): IComponentRegistryHook {
103105
return componentRegistry.getEntityComponents(entityId);
104106
}, []);
105107

108+
const getEntitiesWithComponent = useCallback((componentId: string): EntityId[] => {
109+
return componentRegistry.getEntitiesWithComponent(componentId);
110+
}, []);
111+
112+
const removeComponentsForEntity = useCallback((entityId: EntityId): void => {
113+
return componentRegistry.removeComponentsForEntity(entityId);
114+
}, []);
115+
106116
return {
107117
addComponent,
108118
removeComponent,
109119
hasComponent,
110120
getComponentData,
111121
updateComponent,
112122
listEntityComponents,
123+
getEntitiesWithComponent,
124+
removeComponentsForEntity,
113125
listComponents,
114126
getComponentsByCategory,
115127
registeredComponents,

src/core/hooks/useSceneState.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,18 @@ export const useSceneState = () => {
6565
);
6666

6767
// Create a basic entity with Transform
68-
const createBasicEntity = useCallback(
69-
(name?: string) => {
70-
return createEntity([
71-
{
72-
type: 'Transform',
73-
data: {
74-
position: [0, 0, 0],
75-
rotation: [0, 0, 0],
76-
scale: [1, 1, 1],
77-
},
68+
const createBasicEntity = useCallback(() => {
69+
return createEntity([
70+
{
71+
type: 'Transform',
72+
data: {
73+
position: [0, 0, 0],
74+
rotation: [0, 0, 0],
75+
scale: [1, 1, 1],
7876
},
79-
]);
80-
},
81-
[createEntity],
82-
);
77+
},
78+
]);
79+
}, [createEntity]);
8380

8481
// Create a mesh entity (Transform + MeshRenderer)
8582
const createMeshEntity = useCallback(

src/core/lib/ecs/ComponentManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { addComponent, hasComponent, removeComponent } from 'bitecs';
22

3+
import { emit } from '../events';
34
import { Camera, MeshCollider, MeshRenderer, RigidBody, Transform } from './BitECSComponents';
45
import {
56
getCameraData,

src/core/lib/ecs/ComponentRegistry.ts

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { addComponent, defineComponent, hasComponent, removeComponent } from 'bitecs';
77
import { z } from 'zod';
88

9+
import { emit } from '../events';
910
import { ECSWorld } from './World';
1011
import { EntityId } from './types';
1112

@@ -136,7 +137,6 @@ export class ComponentRegistry {
136137
private components = new Map<string, IComponentDescriptor>();
137138
private bitECSComponents = new Map<string, any>();
138139
private world = ECSWorld.getInstance().getWorld();
139-
private eventListeners = new Set<(event: any) => void>();
140140

141141
private constructor() {}
142142

@@ -253,8 +253,7 @@ export class ComponentRegistry {
253253
descriptor.onAdd?.(entityId, data);
254254

255255
// Emit component added event
256-
this.emitEvent({
257-
type: 'component-added',
256+
emit('component:added', {
258257
entityId,
259258
componentId,
260259
data,
@@ -291,8 +290,7 @@ export class ComponentRegistry {
291290
removeComponent(this.world, bitECSComponent, entityId);
292291

293292
// Emit component removed event
294-
this.emitEvent({
295-
type: 'component-removed',
293+
emit('component:removed', {
296294
entityId,
297295
componentId,
298296
});
@@ -351,8 +349,7 @@ export class ComponentRegistry {
351349
descriptor.deserialize(entityId, updatedData);
352350

353351
// Emit component updated event
354-
this.emitEvent({
355-
type: 'component-updated',
352+
emit('component:updated', {
356353
entityId,
357354
componentId,
358355
data: updatedData,
@@ -566,31 +563,6 @@ export class ComponentRegistry {
566563
clearComponents(): void {
567564
console.warn('clearComponents not fully implemented - use EntityManager.clearEntities()');
568565
}
569-
570-
/**
571-
* Event system for legacy compatibility
572-
*/
573-
addEventListener(listener: (event: any) => void): () => void {
574-
this.eventListeners.add(listener);
575-
576-
// Return unsubscribe function
577-
return () => {
578-
this.eventListeners.delete(listener);
579-
};
580-
}
581-
582-
/**
583-
* Emit event to all listeners
584-
*/
585-
private emitEvent(event: any): void {
586-
this.eventListeners.forEach((listener) => {
587-
try {
588-
listener(event);
589-
} catch (error) {
590-
console.error('Error in component event listener:', error);
591-
}
592-
});
593-
}
594566
}
595567

596568
// Export singleton instance

src/core/lib/events.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ export type CoreEvents = {
1111
'game:scoreChanged': { newScore: number };
1212
'game:itemCollected': { itemType: string; entity: number };
1313
'ui:buttonClicked': { buttonId: string };
14+
15+
// Component system events
16+
'component:added': { entityId: number; componentId: string; data: any };
17+
'component:removed': { entityId: number; componentId: string };
18+
'component:updated': { entityId: number; componentId: string; data: any };
1419
};
1520

1621
export const emitter: Emitter<CoreEvents> = mitt<CoreEvents>();

src/editor/components/menus/AddComponentMenu.tsx

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from 'react-icons/fi';
1313
import { TbCube } from 'react-icons/tb';
1414

15+
import { useEvent } from '@/core/hooks/useEvent';
1516
import { KnownComponentTypes } from '@/core/lib/ecs/IComponent';
1617
import { isValidEntityId } from '@/core/lib/ecs/utils';
1718
import { useComponentManager } from '@/editor/hooks/useComponentManager';
@@ -229,21 +230,24 @@ export const AddComponentMenu: React.FC<IAddComponentMenuProps> = ({
229230
updateEntityComponents();
230231
}, [updateEntityComponents]);
231232

232-
// Listen for component events using the new event system
233-
useEffect(() => {
234-
if (!isValidEntityId(entityId)) return;
235-
236-
const handleComponentEvent = (event: any) => {
237-
if (event.entityId === entityId) {
238-
updateEntityComponents();
239-
}
240-
};
233+
// Listen for component events using the global event system
234+
useEvent('component:added', (event) => {
235+
if (event.entityId === entityId) {
236+
updateEntityComponents();
237+
}
238+
});
241239

242-
// Listen to component events from the ComponentManager
243-
const unsubscribe = componentManager.addEventListener(handleComponentEvent);
240+
useEvent('component:removed', (event) => {
241+
if (event.entityId === entityId) {
242+
updateEntityComponents();
243+
}
244+
});
244245

245-
return unsubscribe;
246-
}, [entityId, componentManager, updateEntityComponents]);
246+
useEvent('component:updated', (event) => {
247+
if (event.entityId === entityId) {
248+
updateEntityComponents();
249+
}
250+
});
247251

248252
// Get available components from KnownComponentTypes
249253
const availableComponents = useMemo(() => {
@@ -694,21 +698,24 @@ export const CompactAddComponentMenu: React.FC<ICompactAddComponentMenuProps> =
694698
updateEntityComponents();
695699
}, [updateEntityComponents]);
696700

697-
// Listen for component events using the new event system
698-
useEffect(() => {
699-
if (!isValidEntityId(entityId)) return;
700-
701-
const handleComponentEvent = (event: any) => {
702-
if (event.entityId === entityId) {
703-
updateEntityComponents();
704-
}
705-
};
701+
// Listen for component events using the global event system
702+
useEvent('component:added', (event) => {
703+
if (event.entityId === entityId) {
704+
updateEntityComponents();
705+
}
706+
});
706707

707-
// Listen to component events from the ComponentManager
708-
const unsubscribe = componentManager.addEventListener(handleComponentEvent);
708+
useEvent('component:removed', (event) => {
709+
if (event.entityId === entityId) {
710+
updateEntityComponents();
711+
}
712+
});
709713

710-
return unsubscribe;
711-
}, [entityId, componentManager, updateEntityComponents]);
714+
useEvent('component:updated', (event) => {
715+
if (event.entityId === entityId) {
716+
updateEntityComponents();
717+
}
718+
});
712719

713720
// Get available components and packs
714721
const availableComponents = useMemo(() => {

src/editor/components/panels/ViewportPanel/ViewportPanel.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Physics } from '@react-three/rapier';
44
import React, { useEffect, useState } from 'react';
55

66
import { GameCameraManager } from '@/core/components/cameras/GameCameraManager';
7+
import { useEvent } from '@/core/hooks/useEvent';
78
import { KnownComponentTypes } from '@/core/lib/ecs/IComponent';
89
import { isValidEntityId } from '@/core/lib/ecs/utils';
910
import { setSelectedCameraEntity } from '@/core/systems/cameraSystem';
@@ -43,21 +44,23 @@ export const ViewportPanel: React.FC<IViewportPanelProps> = ({
4344

4445
// Initial load
4546
updateEntities();
46-
47-
// Listen only to component events that affect rendering
48-
const unsubscribeComponentEvents = componentManager.addEventListener((event) => {
49-
if (event.type === 'component-added' || event.type === 'component-removed') {
50-
if (event.componentId === KnownComponentTypes.TRANSFORM) {
51-
updateEntities();
52-
}
53-
}
54-
});
55-
56-
return () => {
57-
unsubscribeComponentEvents();
58-
};
5947
}, [componentManager]);
6048

49+
// Listen to component events using the global event system
50+
useEvent('component:added', (event) => {
51+
if (event.componentId === KnownComponentTypes.TRANSFORM) {
52+
const entities = componentManager.getEntitiesWithComponent(KnownComponentTypes.TRANSFORM);
53+
setEntityIds(entities);
54+
}
55+
});
56+
57+
useEvent('component:removed', (event) => {
58+
if (event.componentId === KnownComponentTypes.TRANSFORM) {
59+
const entities = componentManager.getEntitiesWithComponent(KnownComponentTypes.TRANSFORM);
60+
setEntityIds(entities);
61+
}
62+
});
63+
6164
// Track if TransformControls is active
6265
const [isTransforming, setIsTransforming] = useState(false);
6366

src/editor/components/panels/ViewportPanel/hooks/useEntityComponents.ts

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useEffect, useMemo, useState } from 'react';
1+
import { useMemo, useState } from 'react';
22

3+
import { useEvent } from '@/core/hooks/useEvent';
34
import { KnownComponentTypes } from '@/core/lib/ecs/IComponent';
45
import { ITransformData } from '@/core/lib/ecs/components/TransformComponent';
56
import { useComponentManager } from '@/editor/hooks/useComponentManager';
@@ -9,27 +10,39 @@ export const useEntityComponents = (entityId: number) => {
910
const [updateTrigger, setUpdateTrigger] = useState(0);
1011

1112
// Listen for relevant component changes only
12-
useEffect(() => {
13-
const relevantComponents = [
14-
KnownComponentTypes.TRANSFORM,
15-
KnownComponentTypes.MESH_RENDERER,
16-
KnownComponentTypes.MESH_COLLIDER,
17-
KnownComponentTypes.RIGID_BODY,
18-
];
19-
20-
const handleComponentChange = (event: any) => {
21-
if (event.entityId === entityId && relevantComponents.includes(event.componentId)) {
22-
// Verify entity still exists before triggering update
23-
const entityExists = componentManager.getComponent(entityId, KnownComponentTypes.TRANSFORM);
24-
if (entityExists) {
25-
setUpdateTrigger((prev) => prev + 1);
26-
}
13+
const relevantComponents = [
14+
KnownComponentTypes.TRANSFORM,
15+
KnownComponentTypes.MESH_RENDERER,
16+
KnownComponentTypes.MESH_COLLIDER,
17+
KnownComponentTypes.RIGID_BODY,
18+
];
19+
20+
// Listen for component events using the global event system
21+
useEvent('component:added', (event) => {
22+
if (event.entityId === entityId && relevantComponents.includes(event.componentId as any)) {
23+
// Verify entity still exists before triggering update
24+
const entityExists = componentManager.getComponent(entityId, KnownComponentTypes.TRANSFORM);
25+
if (entityExists) {
26+
setUpdateTrigger((prev) => prev + 1);
2727
}
28-
};
28+
}
29+
});
2930

30-
const unsubscribe = componentManager.addEventListener(handleComponentChange);
31-
return unsubscribe;
32-
}, [entityId, componentManager]);
31+
useEvent('component:removed', (event) => {
32+
if (event.entityId === entityId && relevantComponents.includes(event.componentId as any)) {
33+
setUpdateTrigger((prev) => prev + 1);
34+
}
35+
});
36+
37+
useEvent('component:updated', (event) => {
38+
if (event.entityId === entityId && relevantComponents.includes(event.componentId as any)) {
39+
// Verify entity still exists before triggering update
40+
const entityExists = componentManager.getComponent(entityId, KnownComponentTypes.TRANSFORM);
41+
if (entityExists) {
42+
setUpdateTrigger((prev) => prev + 1);
43+
}
44+
}
45+
});
3346

3447
// Get transform component (required for all entities)
3548
const transform = componentManager.getComponent<ITransformData>(

0 commit comments

Comments
 (0)