Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading and mocap fixes #9485

Merged
merged 4 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 14 additions & 15 deletions packages/client-core/src/components/World/EngineHooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,21 @@ import { AppLoadingState, AppLoadingStates } from '@etherealengine/engine/src/co
import multiLogger from '@etherealengine/engine/src/common/functions/logger'
import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { EngineActions, EngineState } from '@etherealengine/engine/src/ecs/classes/EngineState'
import { getComponent, removeComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { getComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { NetworkState, addNetwork } from '@etherealengine/engine/src/networking/NetworkState'
import { Network, NetworkTopics, createNetwork } from '@etherealengine/engine/src/networking/classes/Network'
import { NetworkPeerFunctions } from '@etherealengine/engine/src/networking/functions/NetworkPeerFunctions'
import { spawnLocalAvatarInWorld } from '@etherealengine/engine/src/networking/functions/receiveJoinWorld'
import { PortalComponent, PortalState } from '@etherealengine/engine/src/scene/components/PortalComponent'
import { UUIDComponent } from '@etherealengine/engine/src/scene/components/UUIDComponent'
import { addOutgoingTopicIfNecessary, dispatchAction, getMutableState, getState } from '@etherealengine/hyperflux'
import { addOutgoingTopicIfNecessary, dispatchAction, getMutableState } from '@etherealengine/hyperflux'
import { loadEngineInjection } from '@etherealengine/projects/loadEngineInjection'

import { AvatarState } from '@etherealengine/engine/src/avatar/state/AvatarNetworkState'
import { FollowCameraComponent } from '@etherealengine/engine/src/camera/components/FollowCameraComponent'
import { TargetCameraRotationComponent } from '@etherealengine/engine/src/camera/components/TargetCameraRotationComponent'
import { UndefinedEntity } from '@etherealengine/engine/src/ecs/classes/Entity'
import { removeEntity } from '@etherealengine/engine/src/ecs/functions/EntityFunctions'
import { WorldNetworkAction } from '@etherealengine/engine/src/networking/functions/WorldNetworkAction'
import { LinkState } from '@etherealengine/engine/src/scene/components/LinkComponent'
import { InstanceID } from '@etherealengine/engine/src/schemas/networking/instance.schema'
import { ComputedTransformComponent } from '@etherealengine/engine/src/transform/components/ComputedTransformComponent'
import { RouterState } from '../../common/services/RouterService'
import { LocationState } from '../../social/services/LocationService'
import { SocketWebRTCClientNetwork } from '../../transports/SocketWebRTCClientFunctions'
Expand Down Expand Up @@ -128,24 +124,27 @@ export const useLocationSpawnAvatarWithDespawn = () => {

export const despawnSelfAvatar = () => {
const clientEntity = Engine.instance.localClientEntity
console.log('despawnSelfAvatar', clientEntity)
if (!clientEntity) return

const peersCountForUser =
getState(NetworkState).networks[getState(NetworkState).hostIds.world!].users[Engine.instance.userID]?.length
const network = NetworkState.worldNetwork

const peersCountForUser = network?.users?.[Engine.instance.userID]?.length

// if we are the last peer in the world for this user, destroy the object
if (!peersCountForUser || peersCountForUser === 1) {
dispatchAction(WorldNetworkAction.destroyObject({ entityUUID: getComponent(clientEntity, UUIDComponent) }))
}

const cameraEntity = Engine.instance.cameraEntity
if (!cameraEntity) return
/** @todo this logic should be handled by the camera system */
// const cameraEntity = Engine.instance.cameraEntity
// if (!cameraEntity) return

const cameraComputed = getComponent(cameraEntity, ComputedTransformComponent)
removeEntity(cameraComputed.referenceEntity)
removeComponent(cameraEntity, ComputedTransformComponent)
removeComponent(cameraEntity, FollowCameraComponent)
removeComponent(cameraEntity, TargetCameraRotationComponent)
// const cameraComputed = getComponent(cameraEntity, ComputedTransformComponent)
// removeEntity(cameraComputed.referenceEntity)
// removeComponent(cameraEntity, ComputedTransformComponent)
// removeComponent(cameraEntity, FollowCameraComponent)
// removeComponent(cameraEntity, TargetCameraRotationComponent)
}

export const useLinkTeleport = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import {
} from '@etherealengine/client-core/src/common/services/MediaInstanceConnectionService'
import { ChannelService, ChannelState } from '@etherealengine/client-core/src/social/services/ChannelService'
import { LocationState } from '@etherealengine/client-core/src/social/services/LocationService'
import { EngineState } from '@etherealengine/engine/src/ecs/classes/EngineState'
import { NetworkState } from '@etherealengine/engine/src/networking/NetworkState'
import { getMutableState, none, useHookstate } from '@etherealengine/hyperflux'

Expand All @@ -56,7 +55,6 @@ import MessagesMenu from '../user/components/UserMenu/menus/MessagesMenu'
export const WorldInstanceProvisioning = () => {
const locationState = useHookstate(getMutableState(LocationState))
const isUserBanned = locationState.currentLocation.selfUserBanned.value
const engineState = useHookstate(getMutableState(EngineState))

const worldNetwork = NetworkState.worldNetwork
const worldNetworkState = useWorldNetwork()
Expand All @@ -69,8 +67,6 @@ export const WorldInstanceProvisioning = () => {

// Once we have the location, provision the instance server
useEffect(() => {
if (!engineState.sceneLoaded.value || locationInstances.keys.length) return

const currentLocation = locationState.currentLocation.location
const hasJoined = !!worldNetwork

Expand Down Expand Up @@ -106,7 +102,7 @@ export const WorldInstanceProvisioning = () => {
)
}
}
}, [engineState.sceneLoaded, locationState.currentLocation.location, locationInstances.keys])
}, [locationState.currentLocation.location])

// Populate the URL with the room code and instance id
useEffect(() => {
Expand Down
32 changes: 17 additions & 15 deletions packages/client/src/pages/capture/capture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,23 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import React from 'react'
import React, { useEffect } from 'react'
import { useParams } from 'react-router-dom'

import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService'
import {
useLoadEngineWithScene,
useOfflineNetwork,
useOnlineNetwork
} from '@etherealengine/client-core/src/components/World/EngineHooks'
import { useLoadLocation, useLoadScene } from '@etherealengine/client-core/src/components/World/LoadLocationScene'
import { useOfflineNetwork, useOnlineNetwork } from '@etherealengine/client-core/src/components/World/EngineHooks'
import { useRemoveEngineCanvas } from '@etherealengine/client-core/src/hooks/useRemoveEngineCanvas'
import { AuthService } from '@etherealengine/client-core/src/user/services/AuthService'
import { PresentationSystemGroup } from '@etherealengine/engine/src/ecs/functions/EngineFunctions'
import { defineSystem } from '@etherealengine/engine/src/ecs/functions/SystemFunctions'
import { ECSRecordingActions } from '@etherealengine/engine/src/recording/ECSRecordingSystem'
import { defineActionQueue } from '@etherealengine/hyperflux'
import { defineActionQueue, getMutableState, useHookstate } from '@etherealengine/hyperflux'
import CaptureUI from '@etherealengine/ui/src/pages/Capture'

import { LocationService, LocationState } from '@etherealengine/client-core/src/social/services/LocationService'
import '@etherealengine/client-core/src/world/ClientNetworkModule'
import '@etherealengine/engine/src/EngineModule'
import { AppLoadingState, AppLoadingStates } from '@etherealengine/engine/src/common/AppLoadingService'

const ecsRecordingErrorActionQueue = defineActionQueue(ECSRecordingActions.error.matches)

Expand All @@ -57,20 +54,25 @@ const NotifyRecordingErrorSystem = defineSystem({
})

export const CaptureLocation = () => {
const locationState = useHookstate(getMutableState(LocationState))
useRemoveEngineCanvas()

const params = useParams()

const locationName = params?.locationName as string | undefined
const offline = !locationName

useLoadEngineWithScene({ spectate: true })

if (offline) {
useLoadScene({ projectName: 'default-project', sceneName: 'default' })
} else {
useLoadLocation({ locationName: params.locationName! })
}
useEffect(() => {
if (locationName) LocationState.setLocationName(locationName)
getMutableState(AppLoadingState).merge({
state: AppLoadingStates.SUCCESS,
loaded: true
})
}, [])

useEffect(() => {
if (locationState.locationName.value) LocationService.getLocationByName(locationState.locationName.value)
}, [locationState.locationName.value])

if (offline) {
useOfflineNetwork()
Expand Down
1 change: 1 addition & 0 deletions packages/engine/src/common/AppLoadingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const AppLoadingStates = {

type AppLoadingStatesType = (typeof AppLoadingStates)[keyof typeof AppLoadingStates]

/** @deprecated @todo replace with scene loading state directly */
export const AppLoadingState = defineState({
name: 'AppLoadingState',
initial: () => ({
Expand Down
15 changes: 10 additions & 5 deletions packages/engine/src/ecs/functions/EntityTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
getComponent,
getMutableComponent,
getOptionalComponent,
getOptionalMutableComponent,
hasComponent,
removeComponent,
setComponent
Expand Down Expand Up @@ -76,10 +77,14 @@ export const EntityTreeComponent = defineComponent({

// If a previous parentEntity, remove this entity from its children
if (currentParentEntity && currentParentEntity !== json.parentEntity) {
const oldParent = getMutableComponent(currentParentEntity, EntityTreeComponent)
const parentChildIndex = oldParent.children.value.findIndex((child) => child === entity)
const children = oldParent.children.get(NO_PROXY)
oldParent.children.set([...children.slice(0, parentChildIndex), ...children.slice(parentChildIndex + 1)])
if (entityExists(currentParentEntity)) {
const oldParent = getOptionalMutableComponent(currentParentEntity, EntityTreeComponent)
if (oldParent) {
const parentChildIndex = oldParent.children.value.findIndex((child) => child === entity)
const children = oldParent.children.get(NO_PROXY)
oldParent.children.set([...children.slice(0, parentChildIndex), ...children.slice(parentChildIndex + 1)])
}
}
}

// set new data
Expand All @@ -92,7 +97,7 @@ export const EntityTreeComponent = defineComponent({
if (matchesEntityUUID.test(json?.uuid) && !hasComponent(entity, UUIDComponent))
setComponent(entity, UUIDComponent, json.uuid)

if (parentEntity) {
if (parentEntity && entityExists(parentEntity)) {
if (!hasComponent(parentEntity, EntityTreeComponent)) setComponent(parentEntity, EntityTreeComponent)

const parentState = getMutableComponent(parentEntity, EntityTreeComponent)
Expand Down
5 changes: 2 additions & 3 deletions packages/engine/src/interaction/systems/GrabbableSystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,11 @@ export function transferAuthorityOfObjectReceptor(

// since grabbables are all client authoritative, we don't need to recompute this for all users
export function grabbableQueryAll(grabbableEntity: Entity) {
const grabberComponent = getComponent(grabbableEntity, GrabbedComponent)

const grabbedComponent = getComponent(grabbableEntity, GrabbedComponent)
if (!grabbedComponent) return
const attachmentPoint = grabbedComponent.attachmentPoint

const target = getHandTarget(grabberComponent.grabberEntity, attachmentPoint ?? 'right')!
const target = getHandTarget(grabbedComponent.grabberEntity, attachmentPoint ?? 'right')!

const rigidbodyComponent = getComponent(grabbableEntity, RigidBodyComponent)

Expand Down
5 changes: 4 additions & 1 deletion packages/engine/src/mocap/MotionCaptureSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ const execute = () => {
// for now, it is unnecessary to compute anything on the server
if (!isClient) return
const network = NetworkState.worldNetwork
if (!network) return

for (const [peerID, mocapData] of timeSeriesMocapData) {
if (!network?.peers?.[peerID] || timeSeriesMocapLastSeen.get(peerID)! < Date.now() - 1000) {
if (!network.peers[peerID] || timeSeriesMocapLastSeen.get(peerID)! < Date.now() - 1000) {
timeSeriesMocapData.delete(peerID)
timeSeriesMocapLastSeen.delete(peerID)
}
Expand All @@ -126,6 +128,7 @@ const execute = () => {
for (const entity of motionCaptureQuery()) {
const peers = Object.keys(network.peers).find((peerID: PeerID) => timeSeriesMocapData.has(peerID))
const rigComponent = getComponent(entity, AvatarRigComponent)
if (!rigComponent.normalizedRig) continue
const worldHipsParent = rigComponent.normalizedRig.hips.node.parent
if (!peers) {
removeComponent(entity, MotionCaptureRigComponent)
Expand Down
2 changes: 2 additions & 0 deletions packages/engine/src/mocap/poseToInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ let poseHoldTimer = 0

export const evaluatePose = (entity: Entity) => {
const rig = getComponent(entity, AvatarRigComponent).normalizedRig
if (!rig) return

const deltaSeconds = getState(EngineState).deltaSeconds
const pose = getMutableComponent(entity, MotionCapturePoseComponent)
if (!MotionCaptureRigComponent.solvingLowerBody[entity]) return 'none'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class WorldNetworkAction {
ownerId: matchesUserId,
networkId: matchesNetworkId,
newAuthority: matchesPeerID,
$topic: NetworkTopics.world
$topic: NetworkTopics.world,
$cache: true
})
}
19 changes: 3 additions & 16 deletions packages/engine/src/networking/state/EntityNetworkState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ import { NetworkObjectComponent, NetworkObjectOwnedTag } from '../components/Net
import { NetworkPeerFunctions } from '../functions/NetworkPeerFunctions'
import { WorldNetworkAction } from '../functions/WorldNetworkAction'
import { NetworkState } from '../NetworkState'
import {
EntityNetworkState,
receiveRequestAuthorityOverObject,
receiveTransferAuthorityOfObject
} from './EntityNetworkState'
import { EntityNetworkState, receiveRequestAuthorityOverObject } from './EntityNetworkState'

describe('EntityNetworkState', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -277,10 +273,6 @@ describe('EntityNetworkState', () => {
assert.equal(getComponent(networkObjectEntitiesBefore[0], NetworkObjectComponent).authorityPeerID, peerID)
assert.equal(hasComponent(networkObjectEntitiesBefore[0], NetworkObjectOwnedTag), true)

const transferAuthorityOfObjectQueue = ActionFunctions.defineActionQueue(
WorldNetworkAction.transferAuthorityOfObject.matches
)

receiveRequestAuthorityOverObject(
WorldNetworkAction.requestAuthorityOverObject({
$from: userId,
Expand All @@ -293,7 +285,7 @@ describe('EntityNetworkState', () => {

ActionFunctions.applyIncomingActions()

for (const action of transferAuthorityOfObjectQueue()) receiveTransferAuthorityOfObject(action)
await act(() => receiveActions(EntityNetworkState))

const networkObjectEntitiesAfter = networkObjectQuery()
const networkObjectOwnedEntitiesAfter = networkObjectOwnedQuery()
Expand Down Expand Up @@ -351,10 +343,6 @@ describe('EntityNetworkState', () => {
assert.equal(getComponent(networkObjectEntitiesBefore[0], NetworkObjectComponent).authorityPeerID, peerID)
assert.equal(hasComponent(networkObjectEntitiesBefore[0], NetworkObjectOwnedTag), false)

const transferAuthorityOfObjectQueue = ActionFunctions.defineActionQueue(
WorldNetworkAction.transferAuthorityOfObject.matches
)

receiveRequestAuthorityOverObject(
WorldNetworkAction.requestAuthorityOverObject({
$from: userId, // from user
Expand All @@ -366,9 +354,8 @@ describe('EntityNetworkState', () => {
)

applyIncomingActions()
await act(() => receiveActions(EntityNetworkState))

for (const action of transferAuthorityOfObjectQueue()) receiveTransferAuthorityOfObject(action)
await act(() => receiveActions(EntityNetworkState))

const networkObjectEntitiesAfter = networkObjectQuery()
const networkObjectOwnedEntitiesAfter = networkObjectOwnedQuery()
Expand Down
31 changes: 13 additions & 18 deletions packages/engine/src/networking/state/EntityNetworkState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ export const EntityNetworkState = defineState({
})
}
],

[
WorldNetworkAction.destroyObject,
(state, action: typeof WorldNetworkAction.destroyObject.matches._TYPE) => {
Expand All @@ -106,10 +105,23 @@ export const EntityNetworkState = defineState({
if (!entity) return
removeEntity(entity)
}
],
[
WorldNetworkAction.transferAuthorityOfObject,
(state, action: typeof WorldNetworkAction.transferAuthorityOfObject.matches._TYPE) => {
const entity = NetworkObjectComponent.getNetworkObject(action.ownerId, action.networkId)
if (!entity) return
getMutableComponent(entity, NetworkObjectComponent).authorityPeerID.set(action.newAuthority)
}
]
]
})

/**
* Only the transfer needs to be event sourced
* @param action
* @returns
*/
export const receiveRequestAuthorityOverObject = (
action: typeof WorldNetworkAction.requestAuthorityOverObject.matches._TYPE
) => {
Expand All @@ -135,29 +147,12 @@ export const receiveRequestAuthorityOverObject = (
)
}

export const receiveTransferAuthorityOfObject = (
action: typeof WorldNetworkAction.transferAuthorityOfObject.matches._TYPE
) => {
// Authority request can only be processed by owner
if (action.$from !== action.ownerId) return

const entity = NetworkObjectComponent.getNetworkObject(action.ownerId, action.networkId)
if (!entity)
return console.log(
`Warning - tried to get entity belonging to ${action.ownerId} with ID ${action.networkId}, but it doesn't exist`
)

getMutableComponent(entity, NetworkObjectComponent).authorityPeerID.set(action.newAuthority)
}

const requestAuthorityOverObjectQueue = defineActionQueue(WorldNetworkAction.requestAuthorityOverObject.matches)
const transferAuthorityOfObjectQueue = defineActionQueue(WorldNetworkAction.transferAuthorityOfObject.matches)

const execute = () => {
receiveActions(EntityNetworkState)

for (const action of requestAuthorityOverObjectQueue()) receiveRequestAuthorityOverObject(action)
for (const action of transferAuthorityOfObjectQueue()) receiveTransferAuthorityOfObject(action)
}

export const EntityNetworkStateSystem = defineSystem({
Expand Down
2 changes: 1 addition & 1 deletion packages/engine/src/recording/ECSRecordingSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ export const onStartPlayback = async (action: ReturnType<typeof ECSRecordingActi
}
}

if (!UUIDComponent.getEntityByUUID(entityID)) {
if (!UUIDComponent.getEntityByUUID(entityID) && isClone) {
dispatchAction(
AvatarNetworkAction.spawn({
$from: entityID,
Expand Down