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

Fix up e2e bot tests #5706

Merged
merged 10 commits into from
May 5, 2022
3 changes: 2 additions & 1 deletion packages/engine/src/avatar/AvatarControllerSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AvatarControllerComponent } from './components/AvatarControllerComponen
import { XRCameraRotateYComponent } from './components/XRCameraRotateYComponent'
import { setAvatarHeadOpacity } from './functions/avatarFunctions'
import { alignXRCameraPositionWithAvatar, moveAvatar, moveXRAvatar, rotateXRAvatar } from './functions/moveAvatar'
import { respawnAvatar } from './functions/respawnAvatar'
import { accessAvatarInputSettingsState, AvatarInputSettingsReceptor } from './state/AvatarInputSettingsState'

export class AvatarSettings {
Expand Down Expand Up @@ -110,7 +111,7 @@ export default async function AvatarControllerSystem(world: World) {

// TODO: implement scene lower bounds parameter
if (transform.position.y < -10) {
// respawnAvatar(entity)
respawnAvatar(entity)
continue
}
}
Expand Down
12 changes: 6 additions & 6 deletions packages/engine/src/avatar/SkeletonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,12 @@ class SkeletonUtils {

clonedMesh.skeleton.bones = sourceBones.map((bone) => {
if (!cloneLookup.has(bone)) {
console.warn(
'Bone was not cloned',
bone,
'. Common reason is that bones parent is out of clone source',
source
)
// console.warn(
// 'Bone was not cloned',
// bone,
// '. Common reason is that bones parent is out of clone source',
// source
// )
}
return cloneLookup.get(bone)
})
Expand Down
1 change: 1 addition & 0 deletions packages/engine/src/bot/enums/BotHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export enum XRBotHooks {
PressControllerButton = 'XRBotHooks_PressControllerButton',
MoveControllerStick = 'XRBotHooks_MoveControllerStick',
GetXRInputPosition = 'XRBotHooks_GetXRInputPosition',
SetXRInputPosition = 'XRBotHooks_SetXRInputPosition',
TweenXRInputSource = 'XRBotHooks_TweenXRInputSource'
}
2 changes: 2 additions & 0 deletions packages/engine/src/bot/functions/botHookFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
moveControllerStick,
overrideXR,
pressControllerButton,
setXRInputPosition,
startXR,
tweenXRInputSource,
updateController,
Expand All @@ -36,6 +37,7 @@ export const BotHookFunctions = {
[XRBotHooks.PressControllerButton]: pressControllerButton,
[XRBotHooks.MoveControllerStick]: moveControllerStick,
[XRBotHooks.GetXRInputPosition]: getXRInputPosition,
[XRBotHooks.SetXRInputPosition]: setXRInputPosition,
[XRBotHooks.TweenXRInputSource]: tweenXRInputSource
}

Expand Down
40 changes: 38 additions & 2 deletions packages/engine/src/bot/functions/xrBotHookFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,13 @@ export function moveControllerStick(args) {
// )
}

// is in world space, so subtract player pos from it
export function getXRInputPosition() {
const xrInputs = getComponent(useWorld().localClientEntity, XRInputSourceComponent)

const hmd = xrInputs.head.position.toArray().concat(xrInputs.head.quaternion.toArray())
const left = xrInputs.controllerLeft.position.toArray().concat(xrInputs.controllerLeft.quaternion.toArray())
const right = xrInputs.controllerRight.position.toArray().concat(xrInputs.controllerRight.quaternion.toArray())

return {
headInputValue: hmd,
leftControllerInputValue: left,
Expand All @@ -141,7 +142,7 @@ const leftControllerPosition = new Vector3(-0.5, 1.5, -1)
const leftControllerRotation = new Quaternion()
const rightControllerPosition = new Vector3(0.5, 1.5, -1)
const rightControllerRotation = new Quaternion()

// console.warn = () => {} // less annoying warnings
export const getInputSourcePosition = (inputSource: InputSource) => {
switch (inputSource) {
case 'head':
Expand All @@ -164,9 +165,11 @@ export const getInputSourceRotation = (inputSource: InputSource) => {
}

const tweens: any[] = []
let tweensDirty = false

export const sendXRInputData = () => {
tweens.forEach((call) => call())
if (!tweensDirty) return
WebXREventDispatcher.instance.dispatchEvent({
type: 'webxr-pose',
detail: {
Expand Down Expand Up @@ -195,6 +198,38 @@ export const sendXRInputData = () => {
// )
}

type SetXRInputPoseProps = {
head: number[]
left: number[]
right: number[]
}

export function setXRInputPosition(args: SetXRInputPoseProps) {
WebXREventDispatcher.instance.dispatchEvent({
type: 'webxr-pose',
detail: {
position: [args.head[0], args.head[1], args.head[2]],
quaternion: [args.head[3], args.head[4], args.head[5], args.head[6]]
}
})
WebXREventDispatcher.instance.dispatchEvent({
type: 'webxr-input-pose',
detail: {
objectName: 'leftController',
position: [args.left[0], args.left[1], args.left[2]],
quaternion: [args.left[3], args.left[4], args.left[5], args.left[6]]
}
})
WebXREventDispatcher.instance.dispatchEvent({
type: 'webxr-input-pose',
detail: {
objectName: 'rightController',
position: [args.right[0], args.right[1], args.right[2]],
quaternion: [args.right[3], args.right[4], args.right[5], args.right[6]]
}
})
}

export type InputSourceTweenProps = {
objectName: InputSource
time: number // in frames
Expand All @@ -217,6 +252,7 @@ export function tweenXRInputSource(args: InputSourceTweenProps) {
args.callback()
}
counter++
tweensDirty = true
}
tweens.push(tweenFunction)
}
Expand Down
13 changes: 12 additions & 1 deletion packages/engine/src/bot/systems/BotHookSystem.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { isDev } from '@xrengine/common/src/utils/isDev'

import { AvatarInputSchema } from '../../avatar/AvatarInputSchema'
import { Engine } from '../../ecs/classes/Engine'
import { World } from '../../ecs/classes/World'
import { EngineRenderer } from '../../renderer/WebGLRendererSystem'
import { sendXRInputData } from '../functions/xrBotHookFunctions'
import { sendXRInputData, simulateXR } from '../functions/xrBotHookFunctions'

export default async function BotHookSystem(world: World) {
if (isDev) {
const setupBotKey = 'setupBotKey'
AvatarInputSchema.inputMap.set('Semicolon', setupBotKey)
AvatarInputSchema.behaviorMap.set(setupBotKey, () => {
if (!EngineRenderer.instance.xrSession) simulateXR()
})
}

return () => {
if (Engine.instance.isBot && Boolean(EngineRenderer.instance.xrSession)) {
sendXRInputData()
Expand Down
6 changes: 3 additions & 3 deletions packages/projects/default-project/test.scene.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,9 @@
"z": -3.1415926535897922
},
"scale": {
"x": 1,
"y": 1,
"z": 1
"x": 0.01,
"y": 0.01,
"z": 0.01
}
}
},
Expand Down
8 changes: 4 additions & 4 deletions tests/core/webxr.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { setupXR, testWebXR } from '../utils/testWebXR'
import { XREngineBot } from '@xrengine/bot/src/bot'
import { BotHooks } from '@xrengine/engine/src/bot/enums/BotHooks'

import { setupXR, testWebXR } from '../utils/testWebXR'

const bot = new XREngineBot({ name: 'bot-1', verbose: true })

const domain = process.env.APP_HOST
// TODO: load GS & client from static world file instead of having to run independently
const locationName = 'test'
console.log('process.env.HEADLESS', process.env.HEADLESS)

describe('WebXR Bot Tests', () => {

describe.only('WebXR Bot Tests', () => {
before(async () => {
await bot.launchBrowser()
await bot.enterLocation(`https://${domain}/location/${locationName}`)
Expand All @@ -25,4 +25,4 @@ describe('WebXR Bot Tests', () => {
})

testWebXR(bot)
})
})
51 changes: 28 additions & 23 deletions tests/scenes/skystation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,37 @@ describe('Sky Station Bot Tests', () => {
})
})

describe.only('Sky Station Multiple Bot Tests', () => {
describe('Sky Station Multiple Bot Tests', () => {
const bots = Array.apply(null, Array(3)).map((_, index) => new XREngineBot({ name: `bot-${index}`, verbose: true }))
const vector3 = new Vector3()

before(async () => {
for (let bot of bots) {
await bot.launchBrowser()
await bot.enterLocation(`https://${domain}/location/${locationName}`)
await bot.awaitHookPromise(BotHooks.LocationLoaded)
await bot.runHook(BotHooks.InitializeBot)
await bot.delay(1000)
}
})
before(() =>
Promise.all(
bots.map(async (bot) => {
await bot.launchBrowser()
await bot.enterLocation(`https://${domain}/location/${locationName}`)
await bot.awaitHookPromise(BotHooks.LocationLoaded)
await bot.runHook(BotHooks.InitializeBot)
await bot.delay(1000)
})
)
)

after(async () => {
for (let bot of bots) {
await bot.delay(1500)
await bot.quit()
}
})
after(() =>
Promise.all(
bots.map(async (bot) => {
await bot.delay(1500)
await bot.quit()
})
)
)

it('Can spawn multiple bots in sky station world', async () => {
for (let bot of bots) {
await bot.delay(1000)
const pos = await bot.runHook(BotHooks.GetPlayerPosition)
assert(vector3.copy(pos).length() < 45)
}
})
it('Can spawn multiple bots in sky station world', () =>
Promise.all(
bots.map(async (bot) => {
await bot.delay(1000)
const pos = await bot.runHook(BotHooks.GetPlayerPosition)
assert(vector3.copy(pos).length() < 45)
})
))
})
37 changes: 29 additions & 8 deletions tests/utils/testWebXR.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,47 @@
import assert from 'assert'

import type { XREngineBot } from '@xrengine/bot/src/bot'
import { XRBotHooks } from '@xrengine/engine/src/bot/enums/BotHooks'
import { compareArrays } from '@xrengine/engine/src/common/functions/MathRandomFunctions'
import assert from 'assert'

export const setupXR = async (bot: XREngineBot) => {
await bot.runHook(XRBotHooks.OverrideXR)
await bot.runHook(XRBotHooks.StartXR)
}

const testPoses = [
[
[0, 1.6, 0, 0, 1, 0, 0], // head
[-0.5, 1.5, -1, 0, 0, 0, 1], // left
[0.5, 1.5, -1, 0, 0, 0, 1] // right
],
[
[0.1, 1.7, -0.2, -0.5, -0.4, -0.3, 0.2], // head
[-0.4, 1.45, -0.9, 0.6, 0.5, -0.4, 0.3], // left
[0.4, 1.3, -0.8, -0.4, -0.3, 0.2, 0.1] // right
]
]

export const testWebXR = (bot: XREngineBot) => {
it('Web XR works', async () => {
assert(await bot.runHook(XRBotHooks.XRSupported))
assert(await bot.runHook(XRBotHooks.XRInitialized))
})

it('Can detect and move input sources', async () => {
await bot.delay(2000)
const { headInputValue, leftControllerInputValue, rightControllerInputValue } = await bot.runHook(
XRBotHooks.GetXRInputPosition
)
compareArrays(headInputValue, [0, 1.6, 0, 0, 1, 0, 0], 0.01)
compareArrays(leftControllerInputValue, [-0.5, 1.5, -1, 0, 0, 0, 1], 0.01)
compareArrays(rightControllerInputValue, [0.5, 1.5, -1, 0, 0, 0, 1], 0.01)
for (const posesToTest of testPoses) {
await bot.runHook(XRBotHooks.SetXRInputPosition, {
head: posesToTest[0],
left: posesToTest[1],
right: posesToTest[2]
})
await bot.delay(2000)
const { headInputValue, leftControllerInputValue, rightControllerInputValue } = await bot.runHook(
XRBotHooks.GetXRInputPosition
)
compareArrays(headInputValue, posesToTest[0], 0.01)
compareArrays(leftControllerInputValue, posesToTest[1], 0.01)
compareArrays(rightControllerInputValue, posesToTest[2], 0.01)
}
})
}