diff --git a/example/multi-user-3d-web-experience/client/src/index.ts b/example/multi-user-3d-web-experience/client/src/index.ts index f2a6dfdf..c9929576 100644 --- a/example/multi-user-3d-web-experience/client/src/index.ts +++ b/example/multi-user-3d-web-experience/client/src/index.ts @@ -23,6 +23,6 @@ const app = new Networked3dWebExperienceClient(holder, { sprintAnimationFileUrl, }, hdrJpgUrl, - mmlDocumentAddresses: [`${protocol}//${host}/mml-documents/example-mml.html`], + mmlDocuments: [{ url: `${protocol}//${host}/mml-documents/example-mml.html` }], }); app.update(); diff --git a/packages/3d-web-experience-client/src/Networked3dWebExperienceClient.ts b/packages/3d-web-experience-client/src/Networked3dWebExperienceClient.ts index b88114d3..95e5342d 100644 --- a/packages/3d-web-experience-client/src/Networked3dWebExperienceClient.ts +++ b/packages/3d-web-experience-client/src/Networked3dWebExperienceClient.ts @@ -33,11 +33,30 @@ import { } from "mml-web"; import { AudioListener, Euler, Scene, Vector3 } from "three"; +type MMLDocumentConfiguration = { + url: string; + position?: { + x: number; + y: number; + z: number; + }; + rotation?: { + x: number; + y: number; + z: number; + }; + scale?: { + x: number; + y: number; + z: number; + }; +}; + export type Networked3dWebExperienceClientConfig = { sessionToken: string; - chatNetworkAddress: string; + chatNetworkAddress?: string; userNetworkAddress: string; - mmlDocumentAddresses: string[]; + mmlDocuments?: Array; animationConfig: AnimationConfig; hdrJpgUrl: string; }; @@ -275,21 +294,21 @@ export class Networked3dWebExperienceClient { if (this.clientId === null) { return; } - const user = this.userProfiles.get(this.clientId); - if (!user) { - throw new Error("User not found"); - } + if (this.networkChat === null && this.config.chatNetworkAddress) { + const user = this.userProfiles.get(this.clientId); + if (!user) { + throw new Error("User not found"); + } - if (this.textChatUI === null) { - this.textChatUI = new TextChatUI( - this.element, - user.username, - this.sendChatMessageToServer.bind(this), - ); - this.textChatUI.init(); - } + if (this.textChatUI === null) { + this.textChatUI = new TextChatUI( + this.element, + user.username, + this.sendChatMessageToServer.bind(this), + ); + this.textChatUI.init(); + } - if (this.networkChat === null) { this.networkChat = new ChatNetworkingClient({ url: this.config.chatNetworkAddress, sessionToken: this.config.sessionToken, @@ -379,10 +398,33 @@ export class Networked3dWebExperienceClient { this.scene.add(this.mmlCompositionScene.group); setGlobalMMLScene(this.mmlCompositionScene.mmlScene as IMMLScene); - for (const address of this.config.mmlDocumentAddresses) { - const frameElement = document.createElement("m-frame"); - frameElement.setAttribute("src", address); - document.body.appendChild(frameElement); + if (this.config.mmlDocuments) { + for (const mmlDocument of this.config.mmlDocuments) { + const frameElement = document.createElement("m-frame"); + frameElement.setAttribute("src", mmlDocument.url); + if (mmlDocument.position) { + frameElement.setAttribute("x", mmlDocument.position.x.toString()); + frameElement.setAttribute("y", mmlDocument.position.y.toString()); + frameElement.setAttribute("z", mmlDocument.position.z.toString()); + } + if (mmlDocument.rotation) { + frameElement.setAttribute("rx", mmlDocument.rotation.x.toString()); + frameElement.setAttribute("ry", mmlDocument.rotation.y.toString()); + frameElement.setAttribute("rz", mmlDocument.rotation.z.toString()); + } + if (mmlDocument.scale) { + if (mmlDocument.scale.x !== undefined) { + frameElement.setAttribute("sx", mmlDocument.scale.x.toString()); + } + if (mmlDocument.scale.y !== undefined) { + frameElement.setAttribute("sy", mmlDocument.scale.y.toString()); + } + if (mmlDocument.scale.z !== undefined) { + frameElement.setAttribute("sz", mmlDocument.scale.z.toString()); + } + } + document.body.appendChild(frameElement); + } } const mmlProgressManager = this.mmlCompositionScene.mmlScene.getLoadingProgressManager!()!; diff --git a/packages/3d-web-experience-server/src/Networked3dWebExperienceServer.ts b/packages/3d-web-experience-server/src/Networked3dWebExperienceServer.ts index f94aaff6..a4cc1579 100644 --- a/packages/3d-web-experience-server/src/Networked3dWebExperienceServer.ts +++ b/packages/3d-web-experience-server/src/Networked3dWebExperienceServer.ts @@ -5,9 +5,8 @@ import express from "express"; import enableWs from "express-ws"; import WebSocket from "ws"; -import { websocketDirectoryChangeListener } from "../../../utils/websocketDirectoryChangeListener"; - import { MMLDocumentsServer } from "./MMLDocumentsServer"; +import { websocketDirectoryChangeListener } from "./websocketDirectoryChangeListener"; type UserAuthenticator = { generateAuthorizedSessionToken(req: express.Request): string | null; @@ -60,11 +59,13 @@ export class Networked3dWebExperienceServer { this.mmlDocumentsServer = new MMLDocumentsServer(this.config.mmlServing.documentsWatchPath); } - this.chatNetworkingServer = new ChatNetworkingServer({ - getChatUserIdentity: (sessionToken: string) => { - return this.config.userAuthenticator.getClientIdForSessionToken(sessionToken); - }, - }); + if (this.config.chatNetworkPath) { + this.chatNetworkingServer = new ChatNetworkingServer({ + getChatUserIdentity: (sessionToken: string) => { + return this.config.userAuthenticator.getClientIdForSessionToken(sessionToken); + }, + }); + } this.userNetworkingServer = new UserNetworkingServer({ onClientConnect: ( diff --git a/packages/3d-web-experience-server/src/index.ts b/packages/3d-web-experience-server/src/index.ts index 85d15b35..79edcd95 100644 --- a/packages/3d-web-experience-server/src/index.ts +++ b/packages/3d-web-experience-server/src/index.ts @@ -1,2 +1,3 @@ export * from "./MMLDocumentsServer"; export * from "./Networked3dWebExperienceServer"; +export * from "./websocketDirectoryChangeListener"; diff --git a/packages/3d-web-experience-server/src/websocketDirectoryChangeListener.ts b/packages/3d-web-experience-server/src/websocketDirectoryChangeListener.ts new file mode 100644 index 00000000..d49721be --- /dev/null +++ b/packages/3d-web-experience-server/src/websocketDirectoryChangeListener.ts @@ -0,0 +1,25 @@ +import chokidar from "chokidar"; +import enableWs from "express-ws"; +import WebSocket from "ws"; + +export function websocketDirectoryChangeListener( + app: enableWs.Application, + options: { + directory: string; + websocketPath: string; + }, +) { + const listeningClients = new Set(); + chokidar.watch(options.directory).on("all", () => { + for (const client of listeningClients) { + client.send("change"); + } + }); + // Create an event-source that updates whenever the build folder gets modified + app.ws(options.websocketPath, (ws: WebSocket) => { + listeningClients.add(ws); + ws.on("close", () => { + listeningClients.delete(ws); + }); + }); +}