From cb83d5963b901b6b4f360cfaef48cfdc0d4778aa Mon Sep 17 00:00:00 2001 From: Marshall Quander <marshall@quander.me> Date: Wed, 25 Jul 2018 14:42:43 -0700 Subject: [PATCH] Fix and redesign implementation of bot mode --- .../bot}/bot-recording.json | 0 .../avatars => scripts/bot}/bot-recording.mp3 | Bin scripts/bot/run-bot.js | 23 +++++++---- src/assets/stylesheets/loader.scss | 4 +- src/components/avatar-replay.js | 4 ++ src/hub.js | 37 +++++++++--------- src/react-components/ui-root.js | 11 ++++++ webpack.config.js | 12 ------ 8 files changed, 52 insertions(+), 39 deletions(-) rename {src/assets/avatars => scripts/bot}/bot-recording.json (100%) rename {src/assets/avatars => scripts/bot}/bot-recording.mp3 (100%) diff --git a/src/assets/avatars/bot-recording.json b/scripts/bot/bot-recording.json similarity index 100% rename from src/assets/avatars/bot-recording.json rename to scripts/bot/bot-recording.json diff --git a/src/assets/avatars/bot-recording.mp3 b/scripts/bot/bot-recording.mp3 similarity index 100% rename from src/assets/avatars/bot-recording.mp3 rename to scripts/bot/bot-recording.mp3 diff --git a/scripts/bot/run-bot.js b/scripts/bot/run-bot.js index dc3cb0160..2af6b8462 100755 --- a/scripts/bot/run-bot.js +++ b/scripts/bot/run-bot.js @@ -3,10 +3,12 @@ const doc = ` Usage: ./run-bot.js [options] Options: + -h --help Show this screen -u --url=<url> URL -o --host=<host> Hubs host if URL is not specified [default: localhost:8080] -r --room=<room> Room id - -h --help Show this screen + -a --audio=<file> File to replay for the bot's outgoing audio + -d --data=<file> File to replay for the bot's data channel `; const docopt = require("docopt").docopt; @@ -51,13 +53,20 @@ function error(...objs) { await page.evaluate(() => console.log(navigator.userAgent)); let retryCount = 5; let backoff = 1000; - const interact = async () => { + const loadFiles = async () => { try { // Interact with the page so that audio can play. await page.mouse.click(100, 100); - // Signal that the page has been interacted with. - await page.evaluate(() => window.interacted()); - log("Interacted."); + if (options["--audio"]) { + const audioInput = await page.waitForSelector("#bot-audio-input"); + audioInput.uploadFile(options["--audio"]); + log("Uploaded audio file."); + } + if (options["--data"]) { + const dataInput = await page.waitForSelector("#bot-data-input"); + dataInput.uploadFile(options["--data"]); + log("Uploaded data file."); + } } catch (e) { log("Interaction error", e.message); if (retryCount-- < 0) { @@ -67,10 +76,10 @@ function error(...objs) { log("Retrying..."); backoff *= 2; // Retry interaction to start audio playback - setTimeout(interact, backoff); + setTimeout(loadFiles, backoff); } }; - await interact(); + await loadFiles(); } catch (e) { log("Navigation error", e.message); setTimeout(navigate, 1000); diff --git a/src/assets/stylesheets/loader.scss b/src/assets/stylesheets/loader.scss index 448cd2203..74ccb011d 100644 --- a/src/assets/stylesheets/loader.scss +++ b/src/assets/stylesheets/loader.scss @@ -1,4 +1,5 @@ .loader-wrap { + pointer-events: none; position: relative; width: 100px; height: 90px; @@ -6,7 +7,6 @@ .loading-panel { @extend %default-font; - pointer-events: none; color: white; position: absolute; top: 0; @@ -23,7 +23,7 @@ .loading-panel__logo { width: 165px; height: 33px; - margin-top: 20px; + margin: 20px 0; } .loader-center, diff --git a/src/components/avatar-replay.js b/src/components/avatar-replay.js index b1dd13bf7..b739d9760 100644 --- a/src/components/avatar-replay.js +++ b/src/components/avatar-replay.js @@ -27,6 +27,10 @@ AFRAME.registerComponent("avatar-replay", { update: function() { const { camera, leftController, rightController, recordingUrl } = this.data; + if (!recordingUrl) { + return; + } + const fetchRecording = fetch(recordingUrl).then(resp => resp.json()); camera.setAttribute("motion-capture-replayer", { loop: true }); this._setupController(leftController); diff --git a/src/hub.js b/src/hub.js index cf326c149..94a423315 100644 --- a/src/hub.js +++ b/src/hub.js @@ -171,6 +171,7 @@ function mountUI(scene, props = {}) { <UIRoot {...{ scene, + isBotMode, concurrentLoadDetector, disableAutoExitOnConcurrentLoad, forcedVREntryType, @@ -232,7 +233,9 @@ const onReady = async () => { const enterScene = async (mediaStream, enterInVR, hubId) => { const scene = document.querySelector("a-scene"); - scene.classList.add("no-cursor"); + if (!isBotMode) { + scene.classList.add("no-cursor"); + } scene.renderer.sortObjects = true; const playerRig = document.querySelector("#player-rig"); document.querySelector("canvas").classList.remove("blurred"); @@ -374,25 +377,24 @@ const onReady = async () => { playerRig.setAttribute("avatar-replay", { camera: "#player-camera", leftController: "#player-left-controller", - rightController: "#player-right-controller", - recordingUrl: "/assets/avatars/bot-recording.json" + rightController: "#player-right-controller" }); const audioEl = document.createElement("audio"); - audioEl.loop = true; - audioEl.muted = true; - audioEl.crossorigin = "anonymous"; - audioEl.src = "/assets/avatars/bot-recording.mp3"; - document.body.appendChild(audioEl); - - // Wait for runner script to interact with the page so that we can play audio. - const interacted = new Promise(resolve => { - window.interacted = resolve; - }); - const canPlay = new Promise(resolve => { - audioEl.addEventListener("canplay", resolve); - }); - await Promise.all([canPlay, interacted]); + const audioInput = document.querySelector("#bot-audio-input"); + audioInput.onchange = () => { + audioEl.loop = true; + audioEl.muted = true; + audioEl.crossorigin = "anonymous"; + audioEl.src = URL.createObjectURL(audioInput.files[0]); + document.body.appendChild(audioEl); + }; + const dataInput = document.querySelector("#bot-data-input"); + dataInput.onchange = () => { + const url = URL.createObjectURL(dataInput.files[0]); + playerRig.setAttribute("avatar-replay", { recordingUrl: url }); + }; + await new Promise(resolve => audioEl.addEventListener("canplay", resolve)); mediaStream.addTrack(audioEl.captureStream().getAudioTracks()[0]); audioEl.play(); } @@ -470,7 +472,6 @@ const onReady = async () => { const noop = () => {}; // Replace renderer with a noop renderer to reduce bot resource usage. scene.renderer = { setAnimationLoop: noop, render: noop }; - document.body.style.display = "none"; } }); environmentRoot.appendChild(initialEnvironmentEl); diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index b2fab4b99..934a8aee4 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -60,6 +60,7 @@ class UIRoot extends Component { disableAutoExitOnConcurrentLoad: PropTypes.bool, forcedVREntryType: PropTypes.string, enableScreenSharing: PropTypes.bool, + isBotMode: PropTypes.bool, store: PropTypes.object, scene: PropTypes.object, linkChannel: PropTypes.object, @@ -589,6 +590,16 @@ class UIRoot extends Component { ); } + if (this.props.isBotMode) { + return ( + <div className="loading-panel"> + <img className="loading-panel__logo" src="../assets/images/logo.svg" /> + <input type="file" id="bot-audio-input" accept="audio/*" /> + <input type="file" id="bot-data-input" accept="application/json" /> + </div> + ); + } + if (!this.props.initialEnvironmentLoaded || !this.props.availableVREntryTypes || !this.props.hubId) { return ( <IntlProvider locale={lang} messages={messages}> diff --git a/webpack.config.js b/webpack.config.js index b4de849de..bb9be202d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -229,18 +229,6 @@ module.exports = (env, argv) => ({ to: "hub-preview.png" } ]), - new CopyWebpackPlugin([ - { - from: "src/assets/avatars/bot-recording.json", - to: "assets/avatars/bot-recording.json" - } - ]), - new CopyWebpackPlugin([ - { - from: "src/assets/avatars/bot-recording.mp3", - to: "assets/avatars/bot-recording.mp3" - } - ]), // Extract required css and add a content hash. new ExtractTextPlugin({ filename: "assets/stylesheets/[name]-[md5:contenthash:hex:20].css", -- GitLab