Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Add strong entity typings, add missing types to ECS queries
Browse files Browse the repository at this point in the history
  • Loading branch information
speigg committed Apr 12, 2023
1 parent 5e35491 commit b96b64c
Show file tree
Hide file tree
Showing 24 changed files with 554 additions and 475 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
ComponentType,
getComponent,
getMutableComponent,
getOrAddComponent,
hasComponent,
useComponent
} from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
Expand Down
9 changes: 2 additions & 7 deletions packages/editor/src/components/properties/Util.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { Entity } from '@etherealengine/engine/src/ecs/classes/Entity'
import { SceneState } from '@etherealengine/engine/src/ecs/classes/Scene'
import {
Component,
ComponentType,
SerializedComponentType
} from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { Component, SerializedComponentType } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { EntityOrObjectUUID, getEntityNodeArrayFromEntities } from '@etherealengine/engine/src/ecs/functions/EntityTree'
import { iterateEntityNode } from '@etherealengine/engine/src/ecs/functions/EntityTree'
import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent'
Expand Down Expand Up @@ -58,7 +53,7 @@ export const updateProperties = <C extends Component>(
export function traverseScene<T>(
callback: (node: Entity) => T,
predicate: (node: Entity) => boolean = () => true,
snubChildren: boolean = false
snubChildren = false
): T[] {
const result: T[] = []
iterateEntityNode(getState(SceneState).sceneEntity, (node) => result.push(callback(node)), predicate, snubChildren)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { Event, Object3D } from 'three'

import { ComponentJson } from '@etherealengine/common/src/interfaces/SceneInterface'

import {
ComponentMap,
getComponent,
getMutableComponent,
getOptionalComponent,
hasComponent
} from '../../../../ecs/functions/ComponentFunctions'
import { ColliderComponent } from '../../../../scene/components/ColliderComponent'
Expand All @@ -27,16 +24,18 @@ export class EEECSExporterExtension extends ExporterExtension implements GLTFExp
const data = new Array<[string, any]>()
for (const field of gltfLoaded) {
switch (field) {
case 'entity':
const name = getComponent(entity, NameComponent)
case 'entity': {
const name = getOptionalComponent(entity, NameComponent)
data.push(['xrengine.entity', name])
break
default:
const component = ComponentMap.get(field)!
}
default: {
const component = ComponentMap.get(field)! as any
const compData = component.toJSON(entity, getMutableComponent(entity, component))
for (const [field, value] of Object.entries(compData)) {
data.push([`xrengine.${component.name}.${field}`, value])
}
}
}
}
nodeDef.extensions = nodeDef.extensions ?? {}
Expand Down
5 changes: 3 additions & 2 deletions packages/engine/src/avatar/AvatarAnimationSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
DistanceFromCameraComponent,
FrustumCullCameraComponent
} from '../transform/components/DistanceComponents'
import { LocalTransformComponent, TransformComponent } from '../transform/components/TransformComponent'
import { updateGroupChildren } from '../transform/systems/TransformSystem'
import { XRLeftHandComponent, XRRightHandComponent } from '../xr/XRComponents'
import { getCameraMode, isMobileXRHeadset, ReferenceSpace, XRState } from '../xr/XRState'
Expand Down Expand Up @@ -112,7 +113,7 @@ export default async function AvatarAnimationSystem() {
})

Engine.instance.priorityAvatarEntities = priorityQueue.priorityEntities
const filterPriorityEntities = (entity: Entity) =>
const filterPriorityEntities = (entity: Entity<any>) =>
Engine.instance.priorityAvatarEntities.has(entity) || entity === Engine.instance.localClientEntity

const filterFrustumCulledEntities = (entity: Entity) =>
Expand Down Expand Up @@ -339,7 +340,7 @@ export default async function AvatarAnimationSystem() {
for (const entity of loopAnimationEntities) updateGroupChildren(entity)

for (const entity of Engine.instance.priorityAvatarEntities) {
const avatarRig = getComponent(entity, AvatarRigComponent)
const avatarRig = getComponent(entity as Entity<[typeof AvatarRigComponent]>, AvatarRigComponent)
if (avatarRig) {
avatarRig.rig.Hips.updateWorldMatrix(true, true)
avatarRig.helper?.updateMatrixWorld(true)
Expand Down
9 changes: 6 additions & 3 deletions packages/engine/src/avatar/AvatarControllerSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { respawnAvatar } from './functions/respawnAvatar'
import { AvatarInputSettingsReceptor } from './state/AvatarInputSettingsState'

export default async function AvatarControllerSystem() {
const localControllerQuery = defineQuery([AvatarControllerComponent, LocalInputTagComponent])
const localControllerQuery = defineQuery([AvatarControllerComponent, LocalInputTagComponent, UUIDComponent])
const controllerQuery = defineQuery([AvatarControllerComponent])
const sessionChangedActions = createActionQueue(XRAction.sessionChanged.matches)

Expand Down Expand Up @@ -70,8 +70,12 @@ export default async function AvatarControllerSystem() {

const controlledEntity = Engine.instance.localClientEntity

if (hasComponent(controlledEntity, AvatarControllerComponent)) {
if (
hasComponent(controlledEntity, AvatarControllerComponent) &&
hasComponent(controlledEntity, RigidBodyComponent)
) {
const controller = getComponent(controlledEntity, AvatarControllerComponent)
const rigidbody = getComponent(controlledEntity, RigidBodyComponent)

if (controller.movementEnabled) {
/** Support multiple peers controlling the same avatar by detecting movement and overriding network authority.
Expand All @@ -95,7 +99,6 @@ export default async function AvatarControllerSystem() {
}
}

const rigidbody = getComponent(controlledEntity, RigidBodyComponent)
if (rigidbody.position.y < -10) respawnAvatar(controlledEntity)
}
}
Expand Down
11 changes: 6 additions & 5 deletions packages/engine/src/avatar/functions/moveAvatar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getMutableState } from '@etherealengine/hyperflux'

import { destroyEngine, Engine } from '../../ecs/classes/Engine'
import { EngineState } from '../../ecs/classes/EngineState'
import { Entity } from '../../ecs/classes/Entity'
import { getComponent } from '../../ecs/functions/ComponentFunctions'
import { createEngine } from '../../initializeEngine'
import { WorldNetworkAction } from '../../networking/functions/WorldNetworkAction'
Expand Down Expand Up @@ -46,8 +47,8 @@ describe('moveAvatar function tests', () => {

const camera = new PerspectiveCamera(60, 800 / 600, 0.1, 10000)

const velocity = getComponent(entity, RigidBodyComponent).linearVelocity
const avatar = getComponent(entity, AvatarControllerComponent)
const velocity = getComponent(entity as Entity<any>, RigidBodyComponent).linearVelocity
const avatar = getComponent(entity as Entity<any>, AvatarControllerComponent)

avatar.gamepadWorldMovement.setZ(-1)

Expand Down Expand Up @@ -79,7 +80,7 @@ describe('moveAvatar function tests', () => {

const camera = new PerspectiveCamera(60, 800 / 600, 0.1, 10000)

const velocity = getComponent(entity, RigidBodyComponent).linearVelocity
const velocity = getComponent(entity as Entity<any>, RigidBodyComponent).linearVelocity

// velocity starts at 0
strictEqual(velocity.x, 0)
Expand Down Expand Up @@ -114,7 +115,7 @@ describe('moveAvatar function tests', () => {

const camera = new PerspectiveCamera(60, 800 / 600, 0.1, 10000)

const velocity = getComponent(entity, RigidBodyComponent).linearVelocity
const velocity = getComponent(entity as Entity<any>, RigidBodyComponent).linearVelocity

// velocity starts at 0
strictEqual(velocity.x, 0)
Expand Down Expand Up @@ -146,7 +147,7 @@ describe('moveAvatar function tests', () => {

const camera = new PerspectiveCamera(60, 800 / 600, 0.1, 10000)

const velocity = getComponent(entity, RigidBodyComponent).linearVelocity
const velocity = getComponent(entity as Entity<any>, RigidBodyComponent).linearVelocity

// velocity starts at 0
strictEqual(velocity.x, 0)
Expand Down
36 changes: 22 additions & 14 deletions packages/engine/src/ecs/classes/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import { NetworkId } from '@etherealengine/common/src/interfaces/NetworkId'
import { ComponentJson } from '@etherealengine/common/src/interfaces/SceneInterface'

import { GLTFLoader } from '../../assets/loaders/gltf/GLTFLoader'
import { AvatarAnimationComponent } from '../../avatar/components/AvatarAnimationComponent'
import { AvatarComponent } from '../../avatar/components/AvatarComponent'
import { AvatarControllerComponent } from '../../avatar/components/AvatarControllerComponent'
import { CameraComponent } from '../../camera/components/CameraComponent'
import { SceneLoaderType } from '../../common/constants/PrefabFunctionType'
import { nowMilliseconds } from '../../common/functions/nowMilliseconds'
Expand All @@ -31,6 +33,7 @@ import { NetworkObjectComponent } from '../../networking/components/NetworkObjec
import { NetworkState } from '../../networking/NetworkState'
import { SerializationSchema } from '../../networking/serialization/Utils'
import { PhysicsWorld } from '../../physics/classes/Physics'
import { RigidBodyComponent } from '../../physics/components/RigidBodyComponent'
import { addObjectToGroup } from '../../scene/components/GroupComponent'
import { NameComponent } from '../../scene/components/NameComponent'
import { PortalComponent } from '../../scene/components/PortalComponent'
Expand All @@ -41,7 +44,6 @@ import { setTransformComponent, TransformComponent } from '../../transform/compo
import { Widget } from '../../xrui/Widgets'
import {
Component,
ComponentType,
defineQuery,
EntityRemovedComponent,
getComponent,
Expand Down Expand Up @@ -207,7 +209,7 @@ export class Engine {
/**
* The xr origin reference space entity
*/
originEntity: Entity = UndefinedEntity
originEntity: Entity<[typeof TransformComponent]>

/**
* The xr origin group
Expand All @@ -217,7 +219,7 @@ export class Engine {
/**
* The camera entity
*/
cameraEntity: Entity = UndefinedEntity
cameraEntity: Entity<[typeof TransformComponent, typeof CameraComponent]>

/**
* Reference to the three.js camera object.
Expand Down Expand Up @@ -250,13 +252,13 @@ export class Engine {

buttons = {} as Readonly<ButtonInputStateType>

reactiveQueryStates = new Set<{ query: Query; result: State<Entity[]>; components: QueryComponents }>()
reactiveQueryStates = new Set<{ query: Query<any>; result: State<Entity[]>; components: QueryComponents }>()

#entityQuery = defineQuery([Not(EntityRemovedComponent)])
entityQuery = () => this.#entityQuery() as Entity[]

// @todo move to EngineState
activePortal = null as ComponentType<typeof PortalComponent> | null
activePortal = null as typeof PortalComponent._TYPE | null

/**
* Custom systems injected into this world
Expand Down Expand Up @@ -326,7 +328,15 @@ export class Engine {
getUserAvatarEntity(userId: UserId) {
return this.getOwnedNetworkObjectsWithComponent(userId, AvatarComponent).find((eid) => {
return getComponent(eid, AvatarComponent).primary
})!
})! as Entity<
[
typeof NetworkObjectComponent,
typeof AvatarComponent,
typeof AvatarControllerComponent,
typeof AvatarAnimationComponent,
typeof RigidBodyComponent
]
>
}

/**
Expand All @@ -335,12 +345,10 @@ export class Engine {
* @param component
* @returns
*/
getOwnedNetworkObjectWithComponent<T, S extends bitecs.ISchema>(userId: UserId, component: Component<T, S>) {
return (
this.getOwnedNetworkObjects(userId).find((eid) => {
return hasComponent(eid, component)
}) || UndefinedEntity
)
getOwnedNetworkObjectWithComponent<C extends Component>(userId: UserId, component: C) {
return (this.getOwnedNetworkObjects(userId).find((eid) => {
return hasComponent(eid, component)
}) || UndefinedEntity) as Entity<[C, typeof NetworkObjectComponent]>
}

/**
Expand All @@ -349,10 +357,10 @@ export class Engine {
* @param component
* @returns
*/
getOwnedNetworkObjectsWithComponent<T, S extends bitecs.ISchema>(userId: UserId, component: Component<T, S>) {
getOwnedNetworkObjectsWithComponent<C extends Component>(userId: UserId, component: C) {
return this.getOwnedNetworkObjects(userId).filter((eid) => {
return hasComponent(eid, component)
})
}) as any as Entity<[typeof NetworkObjectComponent, C]>[]
}

/** ID of last network created. */
Expand Down
15 changes: 14 additions & 1 deletion packages/engine/src/ecs/classes/Entity.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import { OpaqueType } from '@etherealengine/common/src/interfaces/OpaqueType'

export type Entity = OpaqueType<'entity'> & number
type FilterComponents<T> = T //extends {isComponent:true} ? T : never

/**
* Entity or Entity<[]> means an entity that can NOT be used with any component without type errors
* Entity<[Component1, Component2]> means an entity that is typed to ONLY have Component1 and Component2
*/
export type Entity<C extends readonly any[] = []> = OpaqueType<'entity'> &
number & {
__components: {
[key in FilterComponents<C>[number]['name']]: true
} & {
[key: string]: true
}
}

export const UndefinedEntity = 0 as Entity
5 changes: 3 additions & 2 deletions packages/engine/src/ecs/classes/Scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Color, Texture } from 'three'
import { SceneData } from '@etherealengine/common/src/interfaces/SceneInterface'
import { defineState } from '@etherealengine/hyperflux'

import { UndefinedEntity } from './Entity'
import { EntityTreeComponent } from '../functions/EntityTree'
import { Entity, UndefinedEntity } from './Entity'

/** @todo support multiple scenes */

Expand All @@ -16,7 +17,7 @@ export const SceneState = defineState({
name: 'SceneState',
initial: () => ({
sceneData: null as SceneData | null,
sceneEntity: UndefinedEntity,
sceneEntity: UndefinedEntity as Entity<[typeof EntityTreeComponent]>,
background: null as null | Color | Texture,
sceneMetadataRegistry: {} as Record<string, SceneMetadata<any>>
})
Expand Down
Loading

0 comments on commit b96b64c

Please sign in to comment.