Skip to content

Commit

Permalink
commit
Browse files Browse the repository at this point in the history
  • Loading branch information
johnshaughnessy committed Jul 1, 2023
1 parent f2e69d7 commit 2405498
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .defaults.env
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ THUMBNAIL_SERVER="nearspark-dev.reticulum.io"
ASSET_BUNDLE_SERVER="https://asset-bundles-prod.reticulum.io"

# Comma-separated list of domains which are known to not need CORS proxying
NON_CORS_PROXY_DOMAINS="hubs.local,dev.reticulum.io,hubs-upload-cdn.com"
NON_CORS_PROXY_DOMAINS="hubs.local,hubs-proxy.local,dev.reticulum.io,hubs-upload-cdn.com"

# The root URL under which Hubs expects static assets to be served.
BASE_ASSETS_PATH=/
Expand Down
18 changes: 14 additions & 4 deletions src/components/media-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import {
proxiedUrlFor,
isHubsRoomUrl,
isLocalHubsSceneUrl,
isLocalHubsAvatarUrl
isLocalHubsAvatarUrl,
isHubsDestinationUrl,
isHubsAvatarUrl,
hubsRoomRegex,
localHubsRoomRegex
} from "../utils/media-url-utils";
import { addAnimationComponents } from "../utils/animation";

Expand Down Expand Up @@ -363,10 +367,16 @@ AFRAME.registerComponent("media-loader", {

// We want to resolve and proxy some hubs urls, like rooms and scene links,
// but want to avoid proxying assets in order for this to work in dev environments
const isLocalModelAsset =
isNonCorsProxyDomain(parsedUrl.hostname) && (guessContentType(src) || "").startsWith("model/gltf");
const isLocalAsset =
isNonCorsProxyDomain(parsedUrl.hostname) &&
!(await isHubsDestinationUrl(src)) &&
!(await isHubsAvatarUrl(src)) &&
!src.match(hubsRoomRegex)?.groups.id &&
!src.match(localHubsRoomRegex)?.groups.id;

if (this.data.resolve && !src.startsWith("data:") && !src.startsWith("hubs:") && !isLocalModelAsset) {
console.log("IS LOCAL ASSET?", isLocalAsset, src);

if (this.data.resolve && !src.startsWith("data:") && !src.startsWith("hubs:") && !isLocalAsset) {
const is360 = !!(this.data.mediaOptions.projection && this.data.mediaOptions.projection.startsWith("360"));
const quality = getDefaultResolveQuality(is360);
const result = await resolveUrl(src, quality, version, forceLocalRefresh);
Expand Down
8 changes: 8 additions & 0 deletions src/utils/entity-state-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export function hasSavedEntityState(world: HubsWorld, eid: EntityID) {

export async function createEntityState(hubChannel: HubChannel, world: HubsWorld, eid: EntityID) {
const payload = createEntityStatePayload(world, eid);
return createEntityStateWithPayload(hubChannel, world, payload);
}

export async function createEntityStateWithPayload(
hubChannel: HubChannel,
world: HubsWorld,
payload: CreateEntityStatePayload
) {
// console.log("save_entity_state", payload);
return push(hubChannel, "save_entity_state", payload).catch(err => {
console.warn("Failed to save entity state", err);
Expand Down
67 changes: 44 additions & 23 deletions src/utils/load-legacy-room-objects.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,68 @@
import { pendingLegacyObjectSaves, pendingMessages } from "../bit-systems/networking";
import { messageForLegacyRoomObjects } from "./message-for";
import { StorableMessage } from "./networking-types";
import { messageForLegacyRoomObject } from "./message-for";
import { getReticulumFetchUrl } from "./phoenix-utils";
import { CreateEntityStatePayload, createEntityStateWithPayload } from "./entity-state-utils";

type LegacyRoomObject = any;
type StoredRoomDataNode = LegacyRoomObject | StorableMessage;

type StoredRoomData = {
asset: {
version: "2.0";
generator: "reticulum";
};
scenes: [{ nodes: number[]; name: "Room Objects" }];
nodes: StoredRoomDataNode[];
nodes: LegacyRoomObject[];
extensionsUsed: ["HUBS_components"];
};

export function isStorableMessage(node: any): node is StorableMessage {
return !!(node.version && node.creates && node.updates && node.deletes);
}

// "Legacy Room Objects" are objects that were pinned to the room
// before the release of the Entity State apis.
//
// We do not run any migration on the backend to transform Legacy
// Room Objects into Entity States. This means we need to load
// and handle Legacy Room Objects in the client indefinitely.
//
// First, we download the Legacy Room Objects.
//
// Then, we synthesize `CreateMessage` and `UpdateMessages` for each.
// The only type of Legacy Room Objects that are saved in the database
// are "media" objects: images, videos, models, etc that were previously
// added to the room and then "pinned" by a user. Therefore, it is easy
// for us to synthesize `CreateMessage`s using the "media" prefab.
//
// We queue the synthesized messages so that the network-receive-system
// can handle them like any other normal messages.
//
// Finally, we need each Legacy Room Object to have an associated Entity State
// record in the database. If they don't, then clients will not be able
// to update it. (For example, a user will move a pinned entity, send a request
// to reticulum to update the Entity State, and reticulum will reject the
// update because there's no matching record.)
//
export async function loadLegacyRoomObjects(hubId: string) {
const objectsUrl = getReticulumFetchUrl(`/${hubId}/objects.gltf`) as URL;
const response = await fetch(objectsUrl);
const roomData: StoredRoomData = await response.json();
const legacyRoomObjects: LegacyRoomObject[] = roomData.nodes.filter(node => !isStorableMessage(node));

if (hubId === APP.hub!.hub_id) {
const message = messageForLegacyRoomObjects(legacyRoomObjects);

for (const obj of legacyRoomObjects) {
if (obj.extensions.HUBS_components.pinnable.pinned) {
pendingLegacyObjectSaves.push(obj.name);
}
}

if (message) {
const legacyRoomObjects: LegacyRoomObject[] = roomData.nodes;
legacyRoomObjects.forEach(obj => {
let message = messageForLegacyRoomObject(obj);
let nid = obj.name;
let payload: CreateEntityStatePayload = {
nid,
create_message: message.creates[0],
updates: [
{
root_nid: nid,
nid,
update_message: message.updates[0]
}
]
};
createEntityStateWithPayload(APP.hubChannel!, APP.world, payload);
message.fromClientId = "reticulum";

pendingMessages.push(message);
// TODO All clients must use the new loading path for this to work correctly,
// because all clients must agree on which netcode to use (hubs networking
// systems or networked aframe) for a given object.
}
});
}
}
5 changes: 3 additions & 2 deletions src/utils/media-url-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ async function isHubsServer(url) {

const hubsSceneRegex = /https?:\/\/[^/]+\/scenes\/[a-zA-Z0-9]{7}(?:\/|$)/;
const hubsAvatarRegex = /https?:\/\/[^/]+\/avatars\/(?<id>[a-zA-Z0-9]{7})(?:\/|$)/;
const hubsRoomRegex = /(https?:\/\/)?[^/]+\/(?<id>[a-zA-Z0-9]{7})(?:\/|$)/;
export const hubsRoomRegex = /(https?:\/\/)?[^/]+\/(?<id>[a-zA-Z0-9]{7})(?:\/|$)/;
export const localHubsRoomRegex = /https?:\/\/[^/]+\/hub\.html\?hub_id=(?<id>[a-zA-Z0-9]{7})/;

export const isLocalHubsUrl = async url =>
(await isHubsServer(url)) && new URL(url).origin === document.location.origin;
Expand All @@ -200,7 +201,7 @@ export const isHubsRoomUrl = async url =>
(await isHubsServer(url)) &&
!(await isHubsAvatarUrl(url)) &&
!(await isHubsSceneUrl(url)) &&
url.match(hubsRoomRegex)?.groups.id;
!url.match(hubsRoomRegex)?.groups.id;

export const isHubsDestinationUrl = async url =>
(await isHubsServer(url)) && ((await isHubsSceneUrl(url)) || (await isHubsRoomUrl(url)));
Expand Down
71 changes: 33 additions & 38 deletions src/utils/message-for.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,51 +107,46 @@ export interface LegacyRoomObject {
scale: [number, number, number];
}

export function messageForLegacyRoomObjects(objects: LegacyRoomObject[]) {
export function messageForLegacyRoomObject(obj: LegacyRoomObject) {
const message: Message = {
creates: [],
updates: [],
deletes: []
};

objects.forEach(obj => {
const nid = obj.name;
const initialData: MediaLoaderParams = {
src: obj.extensions.HUBS_components.media.src,
resize: true,
recenter: true,
animateLoad: true,
isObjectMenuTarget: true
};
const createMessage: CreateMessage = {
version: 1,
networkId: nid,
prefabName: "media",
initialData
};
message.creates.push(createMessage);
const nid = obj.name;
const initialData: MediaLoaderParams = {
src: obj.extensions.HUBS_components.media.src,
resize: true,
recenter: true,
animateLoad: true,
isObjectMenuTarget: true
};
const createMessage: CreateMessage = {
version: 1,
networkId: nid,
prefabName: "media",
initialData
};
message.creates.push(createMessage);

const updateMessage: StorableUpdateMessage = {
data: {
"networked-transform": {
version: 1,
data: {
position: obj.translation || [0, 0, 0],
rotation: obj.rotation || [0, 0, 0, 1],
scale: obj.scale || [1, 1, 1]
}
const updateMessage: StorableUpdateMessage = {
data: {
"networked-transform": {
version: 1,
data: {
position: obj.translation || [0, 0, 0],
rotation: obj.rotation || [0, 0, 0, 1],
scale: obj.scale || [1, 1, 1]
}
},
nid,
lastOwnerTime: 1,
timestamp: 1,
owner: "reticulum"
};
message.updates.push(updateMessage);
});
}
},
nid,
lastOwnerTime: 1,
timestamp: 1,
owner: "reticulum"
};
message.updates.push(updateMessage);

if (message.creates.length || message.updates.length) {
return message;
}
return null;
return message;
}

0 comments on commit 2405498

Please sign in to comment.