diff --git a/src/app.ts b/src/app.ts index 4db81cd792..16078b0de2 100644 --- a/src/app.ts +++ b/src/app.ts @@ -65,6 +65,7 @@ export function getScene() { interface HubDescription { hub_id: string; + user_data?: any; } export class App { diff --git a/src/change-hub.js b/src/change-hub.js index 7ae47e9892..e1fcded54c 100644 --- a/src/change-hub.js +++ b/src/change-hub.js @@ -2,9 +2,9 @@ import { getReticulumFetchUrl, hubUrl } from "./utils/phoenix-utils"; import { updateEnvironmentForHub, getSceneUrlForHub, updateUIForHub, remountUI } from "./hub"; import { loadLegacyRoomObjects } from "./utils/load-legacy-room-objects"; import { loadSavedEntityStates } from "./utils/entity-state-utils"; -import qsTruthy from "./utils/qs_truthy"; import { localClientID, pendingMessages, pendingParts } from "./bit-systems/networking"; import { storedUpdates } from "./bit-systems/network-receive-system"; +import { shouldUseNewLoader } from "./utils/bit-utils"; function unloadRoomObjects() { document.querySelectorAll("[pinnable]").forEach(el => { @@ -86,8 +86,8 @@ export async function changeHub(hubId, addToHistory = true, waypoint = null) { NAF.entities.removeRemoteEntities(); await NAF.connection.adapter.disconnect(); - await APP.dialog.disconnect(); - if (!qsTruthy("newLoader")) { + APP.dialog.disconnect(); + if (!shouldUseNewLoader()) { unloadRoomObjects(); } NAF.connection.connectedClients = {}; @@ -106,7 +106,7 @@ export async function changeHub(hubId, addToHistory = true, waypoint = null) { // TODO: With newLoader (and new net code), we need to clear any network state // that we applied to scene-owned entities before transitioning to the new room. // For now, just unload scene even if the room we're going to has the same scene. - qsTruthy("newLoader") || + shouldUseNewLoader() || document.querySelector("#environment-scene").childNodes[0].components["gltf-model-plus"].data.src !== (await getSceneUrlForHub(hub)) ) { @@ -134,7 +134,7 @@ export async function changeHub(hubId, addToHistory = true, waypoint = null) { NAF.connection.adapter.connect() ]); - if (qsTruthy("newLoader")) { + if (shouldUseNewLoader()) { loadSavedEntityStates(APP.hubChannel); loadLegacyRoomObjects(hubId); } else { diff --git a/src/hub.js b/src/hub.js index 1ca8541ad3..7b4128a313 100644 --- a/src/hub.js +++ b/src/hub.js @@ -266,6 +266,7 @@ import { listenForNetworkMessages } from "./utils/listen-for-network-messages"; import { exposeBitECSDebugHelpers } from "./bitecs-debug-helpers"; import { loadLegacyRoomObjects } from "./utils/load-legacy-room-objects"; import { loadSavedEntityStates } from "./utils/entity-state-utils"; +import { shouldUseNewLoader } from "./utils/bit-utils"; const PHOENIX_RELIABLE_NAF = "phx-reliable"; NAF.options.firstSyncSource = PHOENIX_RELIABLE_NAF; @@ -420,7 +421,7 @@ export async function updateEnvironmentForHub(hub, entryManager) { console.log("Updating environment for hub"); const sceneUrl = await getSceneUrlForHub(hub); - if (qsTruthy("newLoader")) { + if (shouldUseNewLoader()) { console.log("Using new loading path for scenes."); swapActiveScene(APP.world, sceneUrl); return; @@ -614,7 +615,7 @@ function handleHubChannelJoined(entryManager, hubChannel, messageDispatch, data) scene.addEventListener( "didConnectToNetworkedScene", () => { - if (qsTruthy("newLoader")) { + if (shouldUseNewLoader()) { loadSavedEntityStates(APP.hubChannel); loadLegacyRoomObjects(hub.hub_id); } else { diff --git a/src/load-media-on-paste-or-drop.ts b/src/load-media-on-paste-or-drop.ts index 9c47f90696..21f854f7d8 100644 --- a/src/load-media-on-paste-or-drop.ts +++ b/src/load-media-on-paste-or-drop.ts @@ -4,6 +4,7 @@ import { guessContentType } from "./utils/media-url-utils"; import { AElement } from "aframe"; import { Vector3 } from "three"; import qsTruthy from "./utils/qs_truthy"; +import { shouldUseNewLoader } from "./utils/bit-utils"; type UploadResponse = { file_id: string; @@ -75,6 +76,7 @@ export async function spawnFromFileList(files: FileList) { } async function onPaste(e: ClipboardEvent) { + if (!shouldUseNewLoader()) return; if (!(AFRAME as any).scenes[0].is("entered")) { return; } @@ -97,6 +99,8 @@ async function onPaste(e: ClipboardEvent) { let lastDebugScene: string; function onDrop(e: DragEvent) { + if (!shouldUseNewLoader()) return; + e.preventDefault(); if (!(AFRAME as any).scenes[0].is("entered")) { @@ -122,7 +126,5 @@ function onDrop(e: DragEvent) { } } -if (qsTruthy("newLoader")) { - document.addEventListener("paste", onPaste); - document.addEventListener("drop", onDrop); -} +document.addEventListener("paste", onPaste); +document.addEventListener("drop", onDrop); diff --git a/src/message-dispatch.js b/src/message-dispatch.js index a20ee6b065..d555db8208 100644 --- a/src/message-dispatch.js +++ b/src/message-dispatch.js @@ -8,10 +8,10 @@ import { EventTarget } from "event-target-shim"; import { ExitReason } from "./react-components/room/ExitedRoomScreen"; import { LogMessageType } from "./react-components/room/ChatSidebar"; import { createNetworkedEntity } from "./utils/create-networked-entity"; -import qsTruthy from "./utils/qs_truthy"; import { add, testAsset, respawn } from "./utils/chat-commands"; import { isLockedDownDemoRoom } from "./utils/hub-utils"; import { loadState, clearState } from "./utils/entity-state-utils"; +import { shouldUseNewLoader } from "./utils/bit-utils"; let uiRoot; // Handles user-entered messages @@ -141,7 +141,7 @@ export default class MessageDispatch extends EventTarget { } break; case "duck": - if (qsTruthy("newLoader")) { + if (shouldUseNewLoader()) { const avatarPov = document.querySelector("#avatar-pov-node").object3D; const eid = createNetworkedEntity(APP.world, "duck"); const obj = APP.world.eid2obj.get(eid); diff --git a/src/react-components/room/hooks/useRoomLoadingState.js b/src/react-components/room/hooks/useRoomLoadingState.js index 4d3cd3c9f2..705b3288db 100644 --- a/src/react-components/room/hooks/useRoomLoadingState.js +++ b/src/react-components/room/hooks/useRoomLoadingState.js @@ -1,7 +1,7 @@ import { useEffect, useReducer, useRef, useCallback } from "react"; import { useIntl, defineMessages } from "react-intl"; import { waitForPreloads } from "../../../utils/preload"; -import qsTruthy from "../../../utils/qs_truthy"; +import { shouldUseNewLoader } from "../../../utils/bit-utils"; function reducer(state, action) { switch (action.type) { @@ -60,7 +60,6 @@ const messages = defineMessages({ } }); -const newLoader = qsTruthy("newLoader"); export function useRoomLoadingState(sceneEl) { // Holds the id of the current const loadingTimeoutRef = useRef(); @@ -88,7 +87,7 @@ export function useRoomLoadingState(sceneEl) { lazyLoadMedia }); // Skip object loading callbacks for the newLoader, since they don't yet fire events we can listen to. - const doneLoadingObjects = lazyLoadMedia || newLoader || allObjectsLoaded; + const doneLoadingObjects = lazyLoadMedia || shouldUseNewLoader() || allObjectsLoaded; const done = sceneEl.is("loaded") || (environmentLoaded && networkConnected && dialogConnected && doneLoadingObjects && donePreloading); diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js index 6dda8ce82b..9d0da98e7e 100644 --- a/src/scene-entry-manager.js +++ b/src/scene-entry-manager.js @@ -24,13 +24,11 @@ import { SOUND_ENTER_SCENE } from "./systems/sound-effects-system"; import { MediaDevices, MediaDevicesEvents } from "./utils/media-devices-utils"; import { addComponent, removeEntity } from "bitecs"; import { MyCameraTool } from "./bit-components"; -import { anyEntityWith } from "./utils/bit-utils"; +import { anyEntityWith, shouldUseNewLoader } from "./utils/bit-utils"; import { moveToSpawnPoint } from "./bit-systems/waypoint"; import { spawnFromFileList, spawnFromUrl } from "./load-media-on-paste-or-drop"; import { isLockedDownDemoRoom } from "./utils/hub-utils"; -const useNewLoader = qsTruthy("newLoader"); - export default class SceneEntryManager { constructor(hubChannel, authChannel, history) { this.hubChannel = hubChannel; @@ -80,7 +78,7 @@ export default class SceneEntryManager { await exit2DInterstitialAndEnterVR(true); } - if (useNewLoader) { + if (shouldUseNewLoader()) { moveToSpawnPoint(APP.world, this.scene.systems["hubs-systems"].characterController); } else { const waypointSystem = this.scene.systems["hubs-systems"].waypointSystem; @@ -244,7 +242,7 @@ export default class SceneEntryManager { }; const spawnMediaInfrontOfPlayer = (src, contentOrigin) => { - if (useNewLoader) { + if (shouldUseNewLoader()) { console.warn( "Spawning newLoader object using `spawnMediaInFrontOfPlayer`. This codepath should likely be made more direct.", src, @@ -293,8 +291,9 @@ export default class SceneEntryManager { this.scene.addEventListener("action_vr_notice_closed", () => forceExitFrom2DInterstitial()); - if (!useNewLoader) { + { document.addEventListener("paste", e => { + if (shouldUseNewLoader()) return; if ( (e.target.matches("input, textarea") || e.target.contentEditable === "true") && document.activeElement === e.target @@ -318,6 +317,7 @@ export default class SceneEntryManager { let lastDebugScene; document.addEventListener("drop", e => { + if (shouldUseNewLoader()) return; e.preventDefault(); if (qsTruthy("debugLocalScene")) { diff --git a/src/systems/character-controller-system.js b/src/systems/character-controller-system.js index f6ccccf014..c4203e9609 100644 --- a/src/systems/character-controller-system.js +++ b/src/systems/character-controller-system.js @@ -13,6 +13,7 @@ import { import { getCurrentPlayerHeight } from "../utils/get-current-player-height"; import qsTruthy from "../utils/qs_truthy"; import { releaseOccupiedWaypoint } from "../bit-systems/waypoint"; +import { shouldUseNewLoader } from "../utils/bit-utils"; //import { m4String } from "../utils/pretty-print"; const NAV_ZONE = "character"; const qsAllowWaypointLerp = qsTruthy("waypointLerp"); @@ -335,7 +336,7 @@ export class CharacterControllerSystem { ) { this.didTeleportSinceLastWaypointTravel = false; this.shouldUnoccupyWaypointsOnceMoving = false; - if (qsTruthy("newLoader")) { + if (shouldUseNewLoader()) { releaseOccupiedWaypoint(); } else { this.waypointSystem.releaseAnyOccupiedWaypoints(); diff --git a/src/systems/userinput/bindings/keyboard-mouse-user.js b/src/systems/userinput/bindings/keyboard-mouse-user.js index 336dade45e..74c74a8d2a 100644 --- a/src/systems/userinput/bindings/keyboard-mouse-user.js +++ b/src/systems/userinput/bindings/keyboard-mouse-user.js @@ -2,7 +2,6 @@ import { paths } from "../paths"; import { sets } from "../sets"; import { xforms } from "./xforms"; import { addSetsToBindings } from "./utils"; -import qsTruthy from "../../../utils/qs_truthy"; // import { Pose } from "../pose"; @@ -27,44 +26,6 @@ const k = name => { return `/keyboard-mouse-user/keyboard-var/${name}`; }; -const videoBindings = [ - { - src: { value: paths.device.mouse.wheel }, - dest: { value: paths.actions.cursor.right.mediaVolumeMod }, - xform: xforms.scale(-0.3), - priority: 1 - } -]; - -if (qsTruthy("newLoader")) { - const priority = 3; - videoBindings.push({ - src: { value: paths.device.mouse.buttonLeft }, - dest: { value: k("mousedown") }, - xform: xforms.rising, - priority - }); - videoBindings.push({ - src: { value: paths.device.mouse.buttonLeft }, - dest: { value: k("mouseup") }, - xform: xforms.falling, - priority - }); - videoBindings.push({ - src: { - rising: k("mousedown"), - falling: k("mouseup") - }, - dest: { - click: paths.actions.cursor.right.togglePlayVideo, - grab: paths.actions.cursor.right.grab, - drop: paths.actions.cursor.right.drop - }, - xform: xforms.clickAndHold(), - priority - }); -} - export const keyboardMouseUserBindings = addSetsToBindings({ [sets.global]: [ { @@ -770,7 +731,42 @@ export const keyboardMouseUserBindings = addSetsToBindings({ xform: xforms.rising } ], - [sets.rightCursorHoveringOnVideo]: videoBindings, + [sets.rightCursorHoveringOnVideo]: [ + { + src: { value: paths.device.mouse.wheel }, + dest: { value: paths.actions.cursor.right.mediaVolumeMod }, + xform: xforms.scale(-0.3), + priority: 1 + }, + // TODO These 3 only technically apply to newLoader but making them conditional is tricky + // This makes old videos have the grab behavior of newLoader ones, but we can live + // with that for now. + { + src: { value: paths.device.mouse.buttonLeft }, + dest: { value: k("mousedown") }, + xform: xforms.rising, + priority: 3 + }, + { + src: { value: paths.device.mouse.buttonLeft }, + dest: { value: k("mouseup") }, + xform: xforms.falling, + priority: 3 + }, + { + src: { + rising: k("mousedown"), + falling: k("mouseup") + }, + dest: { + click: paths.actions.cursor.right.togglePlayVideo, + grab: paths.actions.cursor.right.grab, + drop: paths.actions.cursor.right.drop + }, + xform: xforms.clickAndHold(), + priority: 3 + } + ], [sets.inputFocused]: [ { src: { value: "/device/keyboard" }, diff --git a/src/utils/bit-utils.ts b/src/utils/bit-utils.ts index 2ef14d743a..aaaea7f749 100644 --- a/src/utils/bit-utils.ts +++ b/src/utils/bit-utils.ts @@ -4,6 +4,7 @@ import { Object3D } from "three"; import { HubsWorld } from "../app"; import { findAncestor, traverseSome } from "./three-utils"; import { EntityID } from "./networking-types"; +import qsTruthy from "./qs_truthy"; export type ElOrEid = EntityID | AElement; @@ -48,3 +49,8 @@ export function findChildWithComponent(world: HubsWorld, component: Component, e return childEid; } } + +const forceNewLoader = qsTruthy("newLoader"); +export function shouldUseNewLoader() { + return forceNewLoader || APP.hub?.user_data?.hubsUseNewLoader; +} diff --git a/src/utils/chat-commands.ts b/src/utils/chat-commands.ts index 5cd037ddf7..04fc257ad3 100644 --- a/src/utils/chat-commands.ts +++ b/src/utils/chat-commands.ts @@ -4,7 +4,7 @@ import { HubsWorld } from "../app"; import { moveToSpawnPoint } from "../bit-systems/waypoint"; import { CharacterControllerSystem } from "../systems/character-controller-system"; import { createNetworkedMedia } from "./create-networked-entity"; -import qsTruthy from "./qs_truthy"; +import { shouldUseNewLoader } from "./bit-utils"; function checkFlag(args: string[], flag: string) { return !!args.find(s => s === flag); @@ -103,7 +103,7 @@ export function respawn(world: HubsWorld, scene: AScene, characterController: Ch return; } - if (!qsTruthy("newLoader")) { + if (!shouldUseNewLoader()) { console.error("This command only works with the newLoader query string parameter."); return; }