import { is, Dataspace, Embedded, Reader, Ref, Schemas, Sturdy, Turn } from "@syndicate-lang/core"; import * as html from "@syndicate-lang/html"; import * as timer from "@syndicate-lang/timer"; import * as wsRelay from "@syndicate-lang/ws-relay"; import * as wakeDetector from './wake-detector.js'; import * as Shapes from './gen/shapes.js'; import { md5 } from './md5.js'; import { Engine, Mesh, Scene, TargetCamera, WebXRCamera, } from '@babylonjs/core/Legacy/legacy'; import { activeFloorMeshes, ShapeTree, builder as B } from './shapes.js'; import { startEngine, CreatedScene } from './engine.js'; import { uuid } from './uuid.js'; assertion type SceneHandle(ds: Embedded); function interpretScene(myId: string, scene: Scene, sceneDs: Ref) { at sceneDs { during Shapes.Sprite({ "name": $name: string }) => spawn named `sprite:${name}` { if (name === myId) { console.log('ignoring sprite', name); } else { console.log('+shape', name); on stop console.log('-shape', name); spriteMain(name, scene, sceneDs); } } } } function spriteMain(name: string, scene: Scene, sceneDs: Ref) { at sceneDs { let currentShape = ShapeTree.empty(name, scene); on stop currentShape.remove(); during Shapes.Sprite({ "name": name, "shape": $shape: Shapes.Shape }) => { currentShape = currentShape.reconcile(name, shape); } } } function wsurl(): string { const scheme = (document.location.protocol.toLowerCase() === 'https:') ? 'wss' : 'ws'; return `${scheme}://${document.location.host}/ws`; } function bootApp(ds: Ref, scene: Scene) { spawn named 'app' { at ds { const url = wsurl(); const serverCap = Sturdy.asSturdyRef(new Reader( '').next()); const relayAddr = wsRelay.RelayAddress(Schemas.transportAddress.WebSocket(url)); during wsRelay.Resolved({ "addr": relayAddr, "sturdyref": serverCap, "resolved": $remoteDs_e: Embedded, }) => { const remoteDs = remoteDs_e.embeddedValue; on message wakeDetector.WakeEvent() => { send message wsRelay.ForceRelayDisconnect(relayAddr); } at remoteDs { during SceneHandle($sceneDs_e: Embedded) => { const id = uuid(); const thisFacet = Turn.activeFacet; const sceneDs = sceneDs_e.embeddedValue; interpretScene(id, scene, sceneDs); field position: Shapes.Vector3 = Shapes.Vector3({ x:0, y:0, z:0 }); field rotation: Shapes.Vector3 = Shapes.Vector3({ x:0, y:0, z:0 }); at ds { const refreshPeriod = Math.floor(1000 / 10); on message timer.PeriodicTick(refreshPeriod) => { const camera = scene.cameras[0]; if (camera && camera instanceof TargetCamera) { const newPosition = Shapes.Vector3(camera.position); const newRotation = Shapes.Vector3(camera instanceof WebXRCamera ? camera.rotationQuaternion.toEulerAngles() : camera.rotation); if (!is(Shapes.fromVector3(position.value), Shapes.fromVector3(newPosition))) { position.value = newPosition; } if (!is(Shapes.fromVector3(rotation.value), Shapes.fromVector3(newRotation))) { rotation.value = newRotation; } } } } field email: string = id; const emailInput = document.getElementById('emailInput') as HTMLInputElement; emailInput.value = id; emailInput.addEventListener('keyup', () => thisFacet.turn(() => { email.value = emailInput.value; })); at sceneDs { assert Shapes.Sprite({ name: id, shape: B.nonphysical( B.move(position.value, B.many([ B.move({ x: 0, y: -0.9, z: 0 }, B.rotate({ x: 0, y: rotation.value.y, z: 0 }, B.scale({ x: 0.4, y: 1.4, z: 0.1 }, B.box()))), B.rotate(rotation.value, B.scale({ x: 0.15, y: 0.23, z: 0.18 }, B.many([ B.box(), B.move({ x: 0, y: 0, z: 0.501 }, B.texture( Shapes.TextureSpec.uvAlpha({ path: `https://www.gravatar.com/avatar/${md5(new TextEncoder().encode(email.value.trim()))}?s=256&d=wavatar`, scale: Shapes.Vector2({ x:1, y:1 }), offset: Shapes.Vector2({ x:0, y:0 }), alpha: 1 }), B.rotate({ x: 0, y: Math.PI, z: 0 }, B.plane()))), ]))), ]))), }); } } } } } } } window.addEventListener('load', async () => { const scene = await startEngine( async (_canvas: HTMLCanvasElement, engine: Engine): Promise => ({ scene: new Scene(engine), floorMeshes: () => activeFloorMeshes, })); Dataspace.boot(ds => { html.boot(ds); timer.boot(ds); wsRelay.boot(ds, false); wakeDetector.boot(ds); bootApp(ds, scene); }); });