diff --git a/package-lock.json b/package-lock.json index 6f98ff3..da4b9fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "immersive", - "version": "0.0.7", + "version": "0.0.8-13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immersive", - "version": "0.0.7", + "version": "0.0.8-13", "dependencies": { "@babylonjs/core": "^7.9.0", "@babylonjs/gui": "^7.9.0", @@ -29,6 +29,7 @@ "hls.js": "^1.1.4", "js-crypto-aes": "1.0.6", "loglevel": "^1.9.1", + "meaningful-string": "^1.4.0", "peer-lite": "2.0.2", "pouchdb": "^8.0.1", "pouchdb-find": "^8.0.1", @@ -38,7 +39,9 @@ "rfc4648": "^1.5.3", "round": "^2.0.1", "uint8-to-b64": "^1.0.2", - "uuid": "^9.0.1" + "uuid": "^9.0.1", + "websocket": "^1.0.34", + "websocket-ts": "^2.1.5" }, "devDependencies": { "@types/dom-to-image": "^2.6.7", @@ -1169,6 +1172,28 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/bufferutil/node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1270,6 +1295,18 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1386,6 +1423,43 @@ "errno": "cli.js" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/esbuild": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", @@ -1424,6 +1498,20 @@ "@esbuild/win32-x64": "0.20.2" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -1433,6 +1521,15 @@ "@types/estree": "^1.0.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -1472,6 +1569,14 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -1777,6 +1882,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -2041,6 +2151,11 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "node_modules/meaningful-string": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/meaningful-string/-/meaningful-string-1.4.0.tgz", + "integrity": "sha512-i95AQGuJcVR5mslLf60GyxRIr7EazCgXoR+pFAZCdySAMJ5G3PiLq/dsyv74zKOnLieF1ipeFEp86IMUvcIO3Q==" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2141,6 +2256,11 @@ "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -2959,6 +3079,11 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -2968,6 +3093,14 @@ "node": ">=4" } }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -3017,6 +3150,28 @@ "requires-port": "^1.0.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/utf-8-validate/node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3196,6 +3351,40 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket-ts": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/websocket-ts/-/websocket-ts-2.1.5.tgz", + "integrity": "sha512-rCNl9w6Hsir1azFm/pbjBEFzLD/gi7Th5ZgOxMifB6STUfTSovYAzryWw0TRvSZ1+Qu1Z5Plw4z42UfTNA9idA==" + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -3257,6 +3446,14 @@ "node": ">=0.4" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yocto-queue": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", diff --git a/package.json b/package.json index 59c9129..4fead70 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "vitest", "build": "node versionBump.js && vite build", "preview": "vite preview", + "socket": "node server/server.js", "serve": "node server.js", "serverBuild": "cd server && tsc", "havok": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps" @@ -46,7 +47,10 @@ "js-crypto-aes": "1.0.6", "events": "^3.3.0", "hash-wasm": "4.11.0", - "uint8-to-b64": "^1.0.2" + "uint8-to-b64": "^1.0.2", + "meaningful-string": "^1.4.0", + "websocket-ts": "^2.1.5", + "websocket": "^1.0.34" }, "devDependencies": { "@types/dom-to-image": "^2.6.7", diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..811885f --- /dev/null +++ b/server/server.js @@ -0,0 +1,114 @@ +import websocket from "websocket"; +import http from "http"; +import {sha512} from "hash-wasm"; +import log from "loglevel"; + +async function start() { + const logger = log.getLogger("server"); + logger.setLevel("DEBUG", false); + const WebSocketServer = websocket.server; + //const http = require('http'); + //const sha512 = require('hash-wasm').sha512; + const connections = new Map(); + const server = http.createServer(function (request, response) { + logger.info((new Date()) + ' Received request for ' + request.url); + response.writeHead(404); + response.end(); + }); + server.listen(8080, function () { + logger.info((new Date()) + ' Server is listening on port 8080'); + }); + + const wsServer = new WebSocketServer({ + httpServer: server, + // You should not use autoAcceptConnections for production + // applications, as it defeats all standard cross-origin protection + // facilities built into the protocol and the browser. You should + // *always* verify the connection's origin and decide whether or not + // to accept it. + autoAcceptConnections: false + }); + + function originIsAllowed(origin) { + return origin.indexOf('deepdiagram') > -1; + } + + wsServer.on('request', async (request) => { + if (!originIsAllowed(request.origin)) { + // Make sure we only accept requests from an allowed origin + request.reject(); + logger.error((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); + return; + } + try { + + const connection = request.accept('echo-protocol', request.origin); + const hash = await sha512(connection.socket.remoteAddress + '-' + connection.socket.remotePort); + connections.set(hash, {connection: connection, db: null}); + + + logger.info((new Date()) + ' Connection accepted.', connections.length); + connections.forEach((conn, key) => { + if (key != hash) { + conn.connection.sendUTF('{ "type": "newconnect", "netAttr": "' + hash + '" }'); + } + }); + + + connection.on('message', function (message) { + logger.debug(message); + if (message.type === 'utf8') { + logger.debug('Received Message: ' + message.utf8Data); + connections.forEach((conn, index) => { + const envelope = JSON.parse(message.utf8Data); + if (index !== hash) { + if (envelope.db === conn.db) { + envelope.netAddr = hash; + conn.connection.sendUTF(JSON.stringify(envelope)); + } + } else { + if (!conn.db && envelope.db) { + conn.db = envelope.db; + logger.debug('DB set to ' + envelope.db); + } + } + }); + //connection.sendUTF(message.utf8Data); + } else if (message.type === 'binary') { + logger.debug('Received Binary Message of ' + message.binaryData.length + ' bytes'); + connections.forEach((conn, index) => { + if (index !== hash) { + conn.connection.sendBytes(message.utf8Data); + } + }); + } + + }); + connection.on('close', function (reasonCode, description) { + connections.delete(hash); + connections.forEach((conn, index) => { + conn.connection.sendUTF('{ "type": "close", "netAddr": "' + hash + '" }'); + }); + }); + connection.on('error', function (reasonCode, description) { + connections.delete(hash); + connections.forEach((conn, index) => { + conn.connection.sendUTF('{ "type": "error", "netAddr": "' + hash + '" }'); + }); + logger.info((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.', connections.length); + + }); + setInterval(() => { + const message = `{ "count": ${connections.size} }` + logger.debug(message); + connection.sendUTF(message); + }, 10000); + } catch (err) { + console.log(err); + } + + + }); +} + +start(); \ No newline at end of file diff --git a/src/diagram/diagramManager.ts b/src/diagram/diagramManager.ts index 91b3093..f4119b8 100644 --- a/src/diagram/diagramManager.ts +++ b/src/diagram/diagramManager.ts @@ -9,6 +9,8 @@ import {DiagramMenuManager} from "./diagramMenuManager"; import {DiagramEventObserverMask} from "./types/diagramEventObserverMask"; import {DiagramObject} from "./diagramObject"; import {getMe} from "../util/me"; +import {UserModelType} from "../users/userTypes"; +import {vectoxys} from "./functions/vectorConversion"; export class DiagramManager { @@ -17,10 +19,14 @@ export class DiagramManager { private readonly _controllers: Controllers; private readonly _diagramEntityActionManager: ActionManager; public readonly onDiagramEventObservable: Observable = new Observable(); + public readonly onUserEventObservable: Observable = new Observable(); private readonly _diagramMenuManager: DiagramMenuManager; private readonly _scene: Scene; private readonly _diagramObjects: Map = new Map(); private readonly _me: string; + private _moving: number = 10; + private _i: number = 0; + constructor() { this._me = getMe(); this._scene = DefaultScene.Scene; @@ -30,6 +36,45 @@ export class DiagramManager { this._diagramEntityActionManager = buildEntityActionManager(this._controllers); this.onDiagramEventObservable.add(this.onDiagramEvent, DiagramEventObserverMask.FROM_DB, true, this); + + this.onUserEventObservable.add((user) => { + if (user.id != this._me) { + this._logger.debug('user event', user); + } + }); + window.setInterval(() => { + this._i++; + const platform = this._scene.getMeshByName('platform'); + + if (!platform || !platform.physicsBody) { + return; + } + if (platform.physicsBody) { + if ((this._i % this._moving) == 0) { + this.onUserEventObservable.notifyObservers( + { + id: this._me, + name: 'me', + type: 'user', + base: { + position: vectoxys(platform.absolutePosition), + rotation: vectoxys(platform.absoluteRotationQuaternion.toEulerAngles()), + velocity: vectoxys(platform.physicsBody.getLinearVelocity()) + }, + } + ); + } + if (platform.physicsBody.getLinearVelocity().length() > 0.01) { + this._moving = 1; + } else { + this._moving = 10; + } + + } + + + }, 100); + document.addEventListener('uploadImage', (event: CustomEvent) => { const diagramEntity: DiagramEntity = { template: '#image-template', @@ -88,6 +133,7 @@ export class DiagramManager { public addObject(diagramObject: DiagramObject) { this._diagramObjects.set(diagramObject.diagramEntity.id, diagramObject); } + public get config(): AppConfig { return this._config; } diff --git a/src/integration/functions/syncDoc.ts b/src/integration/functions/syncDoc.ts index df01b87..e4159da 100644 --- a/src/integration/functions/syncDoc.ts +++ b/src/integration/functions/syncDoc.ts @@ -1,12 +1,10 @@ import log from "loglevel"; import {DiagramEntity} from "../../diagram/types/diagramEntity"; import {Observable} from "@babylonjs/core"; -import {UserModelType} from "../../users/userTypes"; import {Encryption} from "../encryption"; import {DiagramEventObserverMask} from "../../diagram/types/diagramEventObserverMask"; export async function syncDoc(info: any, onDBRemoveObservable: Observable, onDBUpdateObservable: Observable, - onUserObservable: Observable, encryption: Encryption, key: string) { const logger = log.getLogger('syncDoc'); logger.debug(info); diff --git a/src/integration/pouchdbPersistenceManager.ts b/src/integration/pouchdbPersistenceManager.ts index 4fceb34..c6331e1 100644 --- a/src/integration/pouchdbPersistenceManager.ts +++ b/src/integration/pouchdbPersistenceManager.ts @@ -12,27 +12,37 @@ import {checkDb} from "./functions/checkDb"; import {UserModelType} from "../users/userTypes"; import {getMe} from "../util/me"; import {Encryption} from "./encryption"; +import {Presence} from "./presence"; +type PasswordEvent = { + detail: string; + +} export class PouchdbPersistenceManager { private _logger: Logger = log.getLogger('PouchdbPersistenceManager'); onDBEntityUpdateObservable: Observable = new Observable(); onDBEntityRemoveObservable: Observable = new Observable(); - onUserObservable: Observable = new Observable(); private db: PouchDB; private remote: PouchDB; private user: string; private _encryption = new Encryption(); private _encKey = null; + private _diagramManager: DiagramManager; + constructor() { document.addEventListener('passwordset', (evt) => { - this._encKey = evt.detail || null; + this._encKey = ((evt as unknown) as PasswordEvent).detail || null; if (this._encKey && typeof (this._encKey) == 'string') { - this.initialize(); + this.initialize().then(() => { + this._logger.debug('Initialized'); + }); } console.log(evt); }); } + public setDiagramManager(diagramManager: DiagramManager) { + this._diagramManager = diagramManager; diagramManager.onDiagramEventObservable.add((evt) => { this._logger.debug(evt); switch (evt.type) { @@ -61,11 +71,7 @@ export class PouchdbPersistenceManager { } }); - this.onUserObservable.add((evt) => { - if (evt.id == getMe()) { - this.updateUser(evt); - } - }); + this.onDBEntityRemoveObservable.add((entity) => { this._logger.debug(entity); @@ -74,21 +80,6 @@ export class PouchdbPersistenceManager { }); } - private async updateUser(user: UserModelType) { - try { - const doc = await this.db.get(user.id); - if (doc) { - const newDoc = {...doc, ...user}; - await this.db.put(newDoc); - } - } catch (err) { - if (err.status == 404) { - await this.db.put({...user, _id: user.id}); - } else { - this._logger.error(err); - } - } - } public async remove(id: string) { if (!id) { return; @@ -252,26 +243,23 @@ export class PouchdbPersistenceManager { salt = dbEntity.doc.encrypted.salt; } const decrypted = await this._encryption.decryptToObject(dbEntity.doc.encrypted.encrypted, dbEntity.doc.encrypted.iv); - if (decrypted.type == 'user') { - this.onUserObservable.notifyObservers(decrypted); - } else { - if (decrypted.id != 'metadata') { - this.onDBEntityUpdateObservable.notifyObservers(decrypted, DiagramEventObserverMask.FROM_DB); - } + + if (decrypted.id != 'metadata') { + this.onDBEntityUpdateObservable.notifyObservers(decrypted, DiagramEventObserverMask.FROM_DB); } + } else { - if (dbEntity.type == 'user') { - this.onUserObservable.notifyObservers(dbEntity.doc); - } else { - if (dbEntity.id != 'metadata') { - this.onDBEntityUpdateObservable.notifyObservers(dbEntity.doc, DiagramEventObserverMask.FROM_DB); - } + + if (dbEntity.id != 'metadata') { + this.onDBEntityUpdateObservable.notifyObservers(dbEntity.doc, DiagramEventObserverMask.FROM_DB); } + + } } - } - if (clear) { - localStorage.removeItem('clearLocal'); + if (clear) { + localStorage.removeItem('clearLocal'); + } } } catch (err) { switch (err.message) { @@ -335,10 +323,14 @@ export class PouchdbPersistenceManager { {auth: {username: remoteUserName, password: password}, skip_setup: true}); const dbInfo = await this.remote.info(); this._logger.debug(dbInfo); - + const presence: Presence = new Presence(getMe(), remoteDbName); + this._diagramManager.onUserEventObservable.add((user: UserModelType) => { + this._logger.debug(user); + presence.sendUser(user); + }, -1, false, this); this.db.sync(this.remote, {live: true, retry: true}) .on('change', (info) => { - syncDoc(info, this.onDBEntityRemoveObservable, this.onDBEntityUpdateObservable, this.onUserObservable, this._encryption, this._encKey); + syncDoc(info, this.onDBEntityRemoveObservable, this.onDBEntityUpdateObservable, this._encryption, this._encKey); }) .on('active', (info) => { this._logger.debug('sync active', info) diff --git a/src/integration/presence.ts b/src/integration/presence.ts new file mode 100644 index 0000000..9276555 --- /dev/null +++ b/src/integration/presence.ts @@ -0,0 +1,134 @@ +import {RingQueue, Websocket, WebsocketBuilder} from "websocket-ts"; +import {getMe} from "../util/me"; +import {UserModelType} from "../users/userTypes"; +import log, {Logger} from "loglevel"; +import {DefaultScene} from "../defaultScene"; +import {Color3, MeshBuilder, StandardMaterial, TransformNode} from "@babylonjs/core"; +import {xyztovec} from "../diagram/functions/vectorConversion"; + + +export class Presence { + private _logger: Logger = log.getLogger("Presence"); + private _ws: Websocket; + private _id: string; + + constructor(id: string = null, db: string = null) { + this._db = db; + if (id == null) { + const localMe = getMe(); + if (localMe != null) { + this._id = localMe; + } + } else { + this._id = id; + } + if (this._id != null) { + this.build(); + } else { + console.error('no user id found'); + } + } + + private _db: string; + + public set db(db: string) { + + this._db = db; + } + + public sendUser(user: UserModelType) { + if (this._ws) { + try { + this._ws.send(JSON.stringify({'type': 'user', 'id': this._id, 'db': this._db, 'user': user})); + } catch (err) { + this._logger.error(err); + } + + } + + } + + private build() { + try { + this._ws = new WebsocketBuilder("wss://presence.deepdiagram.com:443/") + .withBuffer(new RingQueue(2)) + .onOpen(() => { + const me = {'type': 'connect', id: this._id}; + this._ws.send(JSON.stringify(me)); + }) + .onClose(() => this._logger.debug("closed")) + .onError(() => this._logger.debug("error")) + .onMessage((i, ev) => { + this._logger.debug(i); + this._logger.debug(ev.data); + try { + const data = JSON.parse(ev.data); + switch (data.type) { + case 'connect': + break; + case 'ping': + if (data.db && data.id != this._id) { + this._ws.send(JSON.stringify({'type': 'pong', 'id': this._id, 'db': this._db})); + } + break; + case'close': + const scene = DefaultScene.Scene; + const user = scene.getTransformNodeById(data.netAddr); + if (user) { + user.dispose(false, true) + } + break; + case 'pong': + this._logger.debug(data); + break; + case 'user': + if (data.user) { + if (data.user.id) { + const scene = DefaultScene.Scene; + if (scene) { + const user = scene.getTransformNodeById(data.netAddr); + if (user) { + user.position = xyztovec(data.user.base.position); + user.rotation = xyztovec(data.user.base.rotation); + } else { + this._logger.warn('user not found', data.user); + const newUser = MeshBuilder.CreateDisc(data.user.id, {radius: 0.3}, scene); + const node = new TransformNode(data.netAddr, scene); + const material = new StandardMaterial(data.user.id + 'mat', scene); + material.diffuseColor = new Color3(0, 0, 1); + material.backFaceCulling = false; + newUser.material = material; + newUser.parent = node; + newUser.rotation.x = Math.PI / 2; + newUser.position.y = 0.01; + node.position = xyztovec(data.user.base.position); + node.rotation = xyztovec(data.user.base.rotation); + } + } + this._logger.debug('user update', data.user); + } + + } + } + + } catch (e) { + console.error(e); + } + this._logger.debug("message") + }) + .onRetry(() => this._logger.debug("retry")) + .onReconnect(() => this._logger.debug("reconnect")) + .withProtocols("echo-protocol") + .build(); + window.setInterval(() => { + + this._ws.send(JSON.stringify({'type': 'ping', 'id': this._id, db: this._db})); + }, 15000, this); + //this._ws.send(this._id); + } catch (err) { + this._logger.error(err); + return; + } + } + +} \ No newline at end of file diff --git a/src/users/userTypes.ts b/src/users/userTypes.ts index ed21967..e427ecb 100644 --- a/src/users/userTypes.ts +++ b/src/users/userTypes.ts @@ -10,7 +10,8 @@ export type UserModelType = { state?: string, base: { position: XYZType, - rotation: XYZType + rotation: XYZType, + velocity?: XYZType } head?: { position: XYZType, diff --git a/src/vrApp.ts b/src/vrApp.ts index 2289a3f..d22c055 100644 --- a/src/vrApp.ts +++ b/src/vrApp.ts @@ -17,7 +17,6 @@ import {Introduction} from "./tutorial/introduction"; const webGpu = false; log.setLevel('error', false); -log.getLogger('Handle').setLevel('debug'); const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement); export class VrApp {