diff --git a/config/common.pr b/config/common.pr new file mode 100644 index 0000000..d3e29fb --- /dev/null +++ b/config/common.pr @@ -0,0 +1,18 @@ +; Start the server with +; +; syndicate-server -c ./config +; +;--------------------------------------------------------------------------- + +; Expose the gatekeeper on port 9001: + $gatekeeper>> + +; Create a dataspace entity, and register it with the gatekeeper with name `"syndicate"` and an +; empty secret key: +let ?ds = dataspace + + +? [ + > + +] diff --git a/config/nginx.pr b/config/nginx.pr new file mode 100644 index 0000000..0323a1b --- /dev/null +++ b/config/nginx.pr @@ -0,0 +1 @@ + diff --git a/serve.sh b/serve.sh index d6738ff..633b373 100755 --- a/serve.sh +++ b/serve.sh @@ -5,4 +5,4 @@ then openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout dummykey.key -out dummykey.crt cat dummykey.key dummykey.crt > dummykey.pem fi -exec nginx -p `pwd` -c nginx.conf +exec syndicate-server -c ./config diff --git a/src/index.ts b/src/index.ts index d9f73fc..e98849e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,8 @@ +import { Dataspace, Embedded, Reader, Ref, Schemas, Sturdy } from "@syndicate-lang/core"; +import * as html from "@syndicate-lang/html"; +import * as wsRelay from "@syndicate-lang/ws-relay"; +import * as wakeDetector from './wake-detector.js'; + import { Engine, HemisphericLight, @@ -10,6 +15,7 @@ import { import { box, PhotoSemiDome } from './shapes.js'; import { DEFAULT_THICKNESS, room } from './interiors.js'; import { startEngine } from './engine.js'; +import { uuid } from './uuid.js'; async function createScene(_canvas: HTMLCanvasElement, engine: Engine): Promise<{ scene: Scene, @@ -145,6 +151,41 @@ async function createScene(_canvas: HTMLCanvasElement, engine: Engine): Promise< }; } +function wsurl(): string { + const scheme = (document.location.protocol.toLowerCase() === 'https:') ? 'wss' : 'ws'; + return `${scheme}://${document.location.host}/ws`; +} + +function bootApp(ds: Ref) { + spawn named 'app' { + at ds { + const url = wsurl(); + const serverCap = Sturdy.asSturdyRef(new Reader( + '').next()); + const this_instance = uuid(); + 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); + } + } + } + } +} + window.addEventListener('load', async () => { await startEngine(createScene); + Dataspace.boot(ds => { + html.boot(ds); + wsRelay.boot(ds, true); + wakeDetector.boot(ds); + bootApp(ds); + }); }); diff --git a/src/uuid.ts b/src/uuid.ts new file mode 100644 index 0000000..106fb94 --- /dev/null +++ b/src/uuid.ts @@ -0,0 +1,12 @@ +const hexdigit = (c: number) => '0123456789abcdef'[c & 15]; +export function uuid(): string { + // https://datatracker.ietf.org/doc/html/rfc4122#section-4.4 + const bs = new Uint8Array(16); + crypto.getRandomValues(bs); + bs[6] = (bs[6] & 15) | 0x40; + bs[8] = (bs[8] & 63) | 0x80; + const result: string[] = []; + bs.forEach(b => result.push(hexdigit(b >> 4), hexdigit(b))); + const s = result.join(''); + return [s.slice(0,8), s.slice(8,12), s.slice(12,16), s.slice(16,20), s.slice(20)].join('-'); +} diff --git a/src/wake-detector.ts b/src/wake-detector.ts new file mode 100644 index 0000000..661b2c3 --- /dev/null +++ b/src/wake-detector.ts @@ -0,0 +1,26 @@ +// Wake detector - notices when something (such as +// suspension/sleeping!) has caused periodic activities to be +// interrupted, and warns others about it. Inspired by +// http://blog.alexmaccaw.com/javascript-wake-event + +import { Ref, Turn, Observe } from "@syndicate-lang/core"; + +export message type WakeEvent(); + +export function boot(ds: Ref, period0?: number) { + const period = period0 ?? 10000; + at ds { + during Observe({ "pattern": :pattern WakeEvent() }) => spawn named 'WakeDetector' { + const facet = Turn.activeFacet; + let mostRecentTrigger = +(new Date()); + const timerId = setInterval(() => facet.turn(() => { + const now = +(new Date()); + if (now - mostRecentTrigger > period * 1.5) { + send message WakeEvent(); + } + mostRecentTrigger = now; + }), period); + on stop clearInterval(timerId); + } + } +}