@hcikn/colibri 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  Initialize Colibri once with:
11
11
  ```ts
12
12
  import { Colibri } from '@hcikn/colibri';
13
- Colibri.initialize('app_name', 'server_address');
13
+ Colibri.init('app_name', 'server_address');
14
14
  ```
15
15
 
16
16
  For server setup, refer to [colibri-server](../colibri-server/).
@@ -19,9 +19,9 @@ For server setup, refer to [colibri-server](../colibri-server/).
19
19
 
20
20
  ### Web Interface for Logging
21
21
 
22
- Colibri provides a _web logger_ with web interface to send diagnostic data (currently: console logs) to the server. This may be useful for devices (e.g., VR devices, smartphones) where access to the console is not easily available.
22
+ Colibri provides a _web logger_ with web interface to send diagnostic data (currently: console logs) to the server. This may be useful for devices (e.g., VR devices, smartphones) where access to the console is not easily available.
23
23
 
24
- To setup, import the `RemoteLogger` and call its `init()` method. Any subsequent `console` calls should now also appear on your colibri server's web interface, which can be accessed via `http://<your-server-ip>:9011`.
24
+ To setup, import the `RemoteLogger` and call its `init()` method. Any subsequent `console` calls should now also appear on your colibri server's web interface, which can be accessed via `http://<your-server-ip>:9011`.
25
25
 
26
26
  ```ts
27
27
  import { RemoteLogger } from '@hcikn/colibri';
package/dist/index.cjs CHANGED
@@ -140,20 +140,22 @@ var location = "";
140
140
  if (typeof window !== "undefined") {
141
141
  location = window.location.hostname || "";
142
142
  }
143
- var socket;
143
+ var socket = io.connect({
144
+ autoConnect: false
145
+ });
144
146
  var init = function(app, server) {
145
147
  server !== null && server !== void 0 ? server : server = location;
146
148
  if (!server.startsWith("http")) {
147
149
  server = "http://" + server;
148
150
  }
149
151
  server += ":9011";
150
- socket = io.connect(server, {
151
- query: {
152
- app: app,
153
- version: "1"
154
- }
155
- });
156
- console.debug("Connecting to ".concat(server));
152
+ socket.io.uri = server;
153
+ socket.io.opts.query = {
154
+ app: app,
155
+ version: "1"
156
+ };
157
+ socket.connect();
158
+ console.debug("Connecting to colibri server on ".concat(server));
157
159
  socket.on("connect", function() {
158
160
  console.debug("Connected to colibri server on ".concat(server));
159
161
  });
@@ -326,7 +328,17 @@ var init2 = function() {
326
328
  if (_instanceof(obj, Error)) {
327
329
  return obj.message + "\n" + obj.stack;
328
330
  }
329
- return JSON.stringify(obj);
331
+ var cache = [];
332
+ var str = JSON.stringify(obj, function(_, value) {
333
+ if (typeof value === "object" && value !== null) {
334
+ if (cache.indexOf(value) !== -1) {
335
+ return;
336
+ }
337
+ cache.push(value);
338
+ }
339
+ return value;
340
+ }, 2);
341
+ return str;
330
342
  };
331
343
  console.debug = function init2() {
332
344
  for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
@@ -364,7 +376,7 @@ var RemoteLogger = {
364
376
  var import_rxjs2 = require("rxjs");
365
377
  var RegisterModelSync = function(registration) {
366
378
  var name = registration.name || registration.type.name.toLowerCase();
367
- SendMessage("".concat(name), "model::request");
379
+ SendMessage(name, "model::request");
368
380
  console.log("Registering model '".concat(name, "'"));
369
381
  RegisterChannel("".concat(name), function(payload) {
370
382
  var msg = payload;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Networking.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["init","import_rxjs"],"mappings":"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","sourcesContent":["export { Colibri } from './Networking';\r\n\r\nexport * from './Broadcasting';\r\n\r\nexport * from './RemoteLogger';\r\n\r\nexport * from './ModelSynchronization';\r\nexport * from './Synced';\r\nexport * from './SyncModel';\r\n","import { Subject } from 'rxjs';\r\nimport * as io from 'socket.io-client';\r\n\r\nexport interface Message {\r\n channel: string,\r\n command: string,\r\n payload: unknown\r\n}\r\n\r\nconst messageSubject = new Subject<Message>();\r\nexport const Messages = messageSubject.asObservable();\r\n\r\nlet location = '';\r\nif (typeof window !== 'undefined') {\r\n location = window.location.hostname || '';\r\n}\r\nlet socket: io.Socket;\r\n\r\n\r\nconst init = (app: string, server?: string) => {\r\n server ??= location;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += ':9011';\r\n\r\n socket = io.connect(server, { query: { app, version: '1' } });\r\n console.debug(`Connecting to ${server}`);\r\n socket.on('connect', () => {\r\n console.debug(`Connected to colibri server on ${server}`);\r\n });\r\n\r\n socket.onAny((channel, msg) => {\r\n // if (msg.command === 'rpcRequest') {\r\n // const result = CallRpc(msg.channel, msg.payload['parameters']);\r\n // socket.emit(msg.channel, {\r\n // group: APP_NAME,\r\n // command: 'rpcAnswer',\r\n // payload: {\r\n // result,\r\n // rpcId: msg.payload['rpcId']\r\n // }\r\n // });\r\n // }\r\n messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload\r\n });\r\n });\r\n};\r\n\r\nexport const Colibri = {\r\n init\r\n};\r\n\r\n\r\n///////////////////////////\r\n/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (channel: string, command: string, payload: unknown = {}) => {\r\n socket.emit(channel, {\r\n command,\r\n payload\r\n });\r\n};\r\n\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (channel: string, handler: (payload: Message) => void) => {\r\n socket.once(channel, handler);\r\n};\r\n\r\n\r\n//////////////////\r\n/// RPC\r\n//////////////////\r\n// type rpcMethod = (payload: unknown) => unknown;\r\n// const rpcMethods: {[method: string]: rpcMethod } = {};\r\n// export const RegisterRpc = (method: string, fn: rpcMethod): void => {\r\n// rpcMethods[method] = fn;\r\n// };\r\n\r\n// export const SendRpc = async (method: string, payload: any): Promise<any> => {\r\n// // TODO\r\n// return null;\r\n// };\r\n\r\n// const CallRpc = (method: string, payload: any): any => {\r\n// if (rpcMethods[method])\r\n// return rpcMethods[method](payload);\r\n// else {\r\n// console.error(`Unknown rpc method ${method}`);\r\n// return 'error';\r\n// }\r\n// };\r\n\r\n\r\n// for debugging\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n// (window as any).SendMessage = SendMessage;\r\n// messageSubject.subscribe(console.log);\r\n","import { RegisterChannel, SendMessage, UnregisterChannel } from './Networking';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: { [channel: string]: { [ command: string ]: genericCallback[] } } = { };\r\n\r\nconst registerListener = <T>(channel: string, type: string, callback: (val: T) => void) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = { };\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach(cb => cb(msg.payload as T));\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (channel: string, callback: (val: boolean[]) => void) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (channel: string, callback: (val: number[]) => void) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (channel: string, callback: (val: [number, number, number]) => void) => {\r\n registerListener<[number, number, number]>(channel, 'broadcast::vector3', callback);\r\n};\r\n\r\nconst receiveVector3Array = (channel: string, callback: (val: [number, number, number][]) => void) => {\r\n registerListener<[number, number, number][]>(channel, 'broadcast::vector3[]', callback);\r\n};\r\n\r\nconst receiveQuaternion = (channel: string, callback: (val: [number, number, number, number]) => void) => {\r\n registerListener<[number, number, number, number]>(channel, 'broadcast::quaternion', callback);\r\n};\r\n\r\nconst receiveQuaternionArray = (channel: string, callback: (val: [number, number, number, number][]) => void) => {\r\n registerListener<[number, number, number, number][]>(channel, 'broadcast::quaternion[]', callback);\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (channel: string, callback: (val: { [key: string]: unknown }) => void) => {\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool, sendBoolArray,\r\n sendNumber, sendNumberArray,\r\n sendString, sendStringArray,\r\n sendVector3, sendVector3Array,\r\n sendQuaternion, sendQuaternionArray,\r\n sendColor, sendColorArray,\r\n sendJson,\r\n\r\n receiveBool, receiveBoolArray,\r\n receiveNumber, receiveNumberArray,\r\n receiveString, receiveStringArray,\r\n receiveVector3, receiveVector3Array,\r\n receiveQuaternion, receiveQuaternionArray,\r\n receiveColor, receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Networking';\r\n\r\nconst init = () => {\r\n // keep original console commands\r\n const consoleDebug = console.debug;\r\n const consoleLog = console.log;\r\n const consoleWarn = console.warn;\r\n const consoleError = console.error;\r\n\r\n const stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n return JSON.stringify(obj);\r\n };\r\n\r\n // intercept calls from console\r\n console.debug = function(...args) {\r\n consoleDebug.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'debug', [...args].map(stringify).join().trim());\r\n };\r\n console.log = function(...args) {\r\n consoleLog.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'info', [...args].map(stringify).join().trim());\r\n };\r\n console.warn = function(...args) {\r\n consoleWarn.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'warning', [...args].map(stringify).join().trim());\r\n };\r\n console.error = function(...args) {\r\n consoleError.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'error', [...args].map(stringify).join().trim());\r\n };\r\n};\r\n\r\n\r\nexport const RemoteLogger = {\r\n init\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Networking';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new(id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [ Observable<T[]>, (model: T) => void ];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(registration: ModelSyncRegistration<T>): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(`${name}`, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find(m => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', newModel.toJson(changes));\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find(m => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter(m => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [ models.asObservable(), registerModel ];\r\n};\r\n\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Networking.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["init","import_rxjs"],"mappings":"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","sourcesContent":["export { Colibri } from './Networking';\r\n\r\nexport * from './Broadcasting';\r\n\r\nexport * from './RemoteLogger';\r\n\r\nexport * from './ModelSynchronization';\r\nexport * from './Synced';\r\nexport * from './SyncModel';\r\n","import { Subject } from 'rxjs';\r\nimport * as io from 'socket.io-client';\r\n\r\nexport interface Message {\r\n channel: string,\r\n command: string,\r\n payload: unknown\r\n}\r\n\r\nconst messageSubject = new Subject<Message>();\r\nexport const Messages = messageSubject.asObservable();\r\n\r\nlet location = '';\r\nif (typeof window !== 'undefined') {\r\n location = window.location.hostname || '';\r\n}\r\n\r\nconst socket = io.connect({\r\n autoConnect: false\r\n});\r\n\r\n\r\nconst init = (app: string, server?: string) => {\r\n server ??= location;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += ':9011';\r\n\r\n // FIXME: terrible workaround because socket.io marked uri as readonly\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (socket.io as any).uri = server;\r\n socket.io.opts.query = { app, version: '1' };\r\n socket.connect();\r\n\r\n console.debug(`Connecting to colibri server on ${server}`);\r\n socket.on('connect', () => {\r\n console.debug(`Connected to colibri server on ${server}`);\r\n });\r\n\r\n socket.onAny((channel, msg) => {\r\n // if (msg.command === 'rpcRequest') {\r\n // const result = CallRpc(msg.channel, msg.payload['parameters']);\r\n // socket.emit(msg.channel, {\r\n // group: APP_NAME,\r\n // command: 'rpcAnswer',\r\n // payload: {\r\n // result,\r\n // rpcId: msg.payload['rpcId']\r\n // }\r\n // });\r\n // }\r\n messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload\r\n });\r\n });\r\n};\r\n\r\nexport const Colibri = {\r\n init\r\n};\r\n\r\n\r\n///////////////////////////\r\n/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (channel: string, command: string, payload: unknown = {}) => {\r\n socket.emit(channel, {\r\n command,\r\n payload\r\n });\r\n};\r\n\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (channel: string, handler: (payload: Message) => void) => {\r\n socket.once(channel, handler);\r\n};\r\n\r\n\r\n//////////////////\r\n/// RPC\r\n//////////////////\r\n// type rpcMethod = (payload: unknown) => unknown;\r\n// const rpcMethods: {[method: string]: rpcMethod } = {};\r\n// export const RegisterRpc = (method: string, fn: rpcMethod): void => {\r\n// rpcMethods[method] = fn;\r\n// };\r\n\r\n// export const SendRpc = async (method: string, payload: any): Promise<any> => {\r\n// // TODO\r\n// return null;\r\n// };\r\n\r\n// const CallRpc = (method: string, payload: any): any => {\r\n// if (rpcMethods[method])\r\n// return rpcMethods[method](payload);\r\n// else {\r\n// console.error(`Unknown rpc method ${method}`);\r\n// return 'error';\r\n// }\r\n// };\r\n\r\n\r\n// for debugging\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n// (window as any).SendMessage = SendMessage;\r\n// messageSubject.subscribe(console.log);\r\n","import { RegisterChannel, SendMessage } from './Networking';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: { [channel: string]: { [ command: string ]: genericCallback[] } } = { };\r\n\r\nconst registerListener = <T>(channel: string, type: string, callback: (val: T) => void) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = { };\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach(cb => cb(msg.payload as T));\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (channel: string, callback: (val: boolean[]) => void) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (channel: string, callback: (val: number[]) => void) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (channel: string, callback: (val: [number, number, number]) => void) => {\r\n registerListener<[number, number, number]>(channel, 'broadcast::vector3', callback);\r\n};\r\n\r\nconst receiveVector3Array = (channel: string, callback: (val: [number, number, number][]) => void) => {\r\n registerListener<[number, number, number][]>(channel, 'broadcast::vector3[]', callback);\r\n};\r\n\r\nconst receiveQuaternion = (channel: string, callback: (val: [number, number, number, number]) => void) => {\r\n registerListener<[number, number, number, number]>(channel, 'broadcast::quaternion', callback);\r\n};\r\n\r\nconst receiveQuaternionArray = (channel: string, callback: (val: [number, number, number, number][]) => void) => {\r\n registerListener<[number, number, number, number][]>(channel, 'broadcast::quaternion[]', callback);\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (channel: string, callback: (val: { [key: string]: unknown }) => void) => {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool, sendBoolArray,\r\n sendNumber, sendNumberArray,\r\n sendString, sendStringArray,\r\n sendVector3, sendVector3Array,\r\n sendQuaternion, sendQuaternionArray,\r\n sendColor, sendColorArray,\r\n sendJson,\r\n\r\n receiveBool, receiveBoolArray,\r\n receiveNumber, receiveNumberArray,\r\n receiveString, receiveStringArray,\r\n receiveVector3, receiveVector3Array,\r\n receiveQuaternion, receiveQuaternionArray,\r\n receiveColor, receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Networking';\r\n\r\nconst init = () => {\r\n // keep original console commands\r\n const consoleDebug = console.debug;\r\n const consoleLog = console.log;\r\n const consoleWarn = console.warn;\r\n const consoleError = console.error;\r\n\r\n const stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n const cache: unknown[] = [];\r\n const str = JSON.stringify(obj, (_, value: unknown) => {\r\n if (typeof value === 'object' && value !== null) {\r\n if (cache.indexOf(value) !== -1) {\r\n // Circular reference found, discard key\r\n return;\r\n }\r\n // Store value in our collection\r\n cache.push(value);\r\n }\r\n return value;\r\n }, 2);\r\n return str;\r\n };\r\n\r\n // intercept calls from console\r\n console.debug = function (...args) {\r\n consoleDebug.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'debug', [...args].map(stringify).join().trim());\r\n };\r\n console.log = function (...args) {\r\n consoleLog.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'info', [...args].map(stringify).join().trim());\r\n };\r\n console.warn = function (...args) {\r\n consoleWarn.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'warning', [...args].map(stringify).join().trim());\r\n };\r\n console.error = function (...args) {\r\n consoleError.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'error', [...args].map(stringify).join().trim());\r\n };\r\n};\r\n\r\n\r\nexport const RemoteLogger = {\r\n init\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Networking';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new(id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [ Observable<T[]>, (model: T) => void ];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(registration: ModelSyncRegistration<T>): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(name, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find(m => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', newModel.toJson(changes));\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find(m => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter(m => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [ models.asObservable(), registerModel ];\r\n};\r\n\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
package/dist/index.js CHANGED
@@ -58,20 +58,22 @@ var location = "";
58
58
  if (typeof window !== "undefined") {
59
59
  location = window.location.hostname || "";
60
60
  }
61
- var socket;
61
+ var socket = io.connect({
62
+ autoConnect: false
63
+ });
62
64
  var init = function(app, server) {
63
65
  server !== null && server !== void 0 ? server : server = location;
64
66
  if (!server.startsWith("http")) {
65
67
  server = "http://" + server;
66
68
  }
67
69
  server += ":9011";
68
- socket = io.connect(server, {
69
- query: {
70
- app: app,
71
- version: "1"
72
- }
73
- });
74
- console.debug("Connecting to ".concat(server));
70
+ socket.io.uri = server;
71
+ socket.io.opts.query = {
72
+ app: app,
73
+ version: "1"
74
+ };
75
+ socket.connect();
76
+ console.debug("Connecting to colibri server on ".concat(server));
75
77
  socket.on("connect", function() {
76
78
  console.debug("Connected to colibri server on ".concat(server));
77
79
  });
@@ -244,7 +246,17 @@ var init2 = function() {
244
246
  if (_instanceof(obj, Error)) {
245
247
  return obj.message + "\n" + obj.stack;
246
248
  }
247
- return JSON.stringify(obj);
249
+ var cache = [];
250
+ var str = JSON.stringify(obj, function(_, value) {
251
+ if (typeof value === "object" && value !== null) {
252
+ if (cache.indexOf(value) !== -1) {
253
+ return;
254
+ }
255
+ cache.push(value);
256
+ }
257
+ return value;
258
+ }, 2);
259
+ return str;
248
260
  };
249
261
  console.debug = function init2() {
250
262
  for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
@@ -282,7 +294,7 @@ var RemoteLogger = {
282
294
  import { BehaviorSubject } from "rxjs";
283
295
  var RegisterModelSync = function(registration) {
284
296
  var name = registration.name || registration.type.name.toLowerCase();
285
- SendMessage("".concat(name), "model::request");
297
+ SendMessage(name, "model::request");
286
298
  console.log("Registering model '".concat(name, "'"));
287
299
  RegisterChannel("".concat(name), function(payload) {
288
300
  var msg = payload;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Networking.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["init","Subject"],"mappings":"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","sourcesContent":["import { Subject } from 'rxjs';\r\nimport * as io from 'socket.io-client';\r\n\r\nexport interface Message {\r\n channel: string,\r\n command: string,\r\n payload: unknown\r\n}\r\n\r\nconst messageSubject = new Subject<Message>();\r\nexport const Messages = messageSubject.asObservable();\r\n\r\nlet location = '';\r\nif (typeof window !== 'undefined') {\r\n location = window.location.hostname || '';\r\n}\r\nlet socket: io.Socket;\r\n\r\n\r\nconst init = (app: string, server?: string) => {\r\n server ??= location;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += ':9011';\r\n\r\n socket = io.connect(server, { query: { app, version: '1' } });\r\n console.debug(`Connecting to ${server}`);\r\n socket.on('connect', () => {\r\n console.debug(`Connected to colibri server on ${server}`);\r\n });\r\n\r\n socket.onAny((channel, msg) => {\r\n // if (msg.command === 'rpcRequest') {\r\n // const result = CallRpc(msg.channel, msg.payload['parameters']);\r\n // socket.emit(msg.channel, {\r\n // group: APP_NAME,\r\n // command: 'rpcAnswer',\r\n // payload: {\r\n // result,\r\n // rpcId: msg.payload['rpcId']\r\n // }\r\n // });\r\n // }\r\n messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload\r\n });\r\n });\r\n};\r\n\r\nexport const Colibri = {\r\n init\r\n};\r\n\r\n\r\n///////////////////////////\r\n/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (channel: string, command: string, payload: unknown = {}) => {\r\n socket.emit(channel, {\r\n command,\r\n payload\r\n });\r\n};\r\n\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (channel: string, handler: (payload: Message) => void) => {\r\n socket.once(channel, handler);\r\n};\r\n\r\n\r\n//////////////////\r\n/// RPC\r\n//////////////////\r\n// type rpcMethod = (payload: unknown) => unknown;\r\n// const rpcMethods: {[method: string]: rpcMethod } = {};\r\n// export const RegisterRpc = (method: string, fn: rpcMethod): void => {\r\n// rpcMethods[method] = fn;\r\n// };\r\n\r\n// export const SendRpc = async (method: string, payload: any): Promise<any> => {\r\n// // TODO\r\n// return null;\r\n// };\r\n\r\n// const CallRpc = (method: string, payload: any): any => {\r\n// if (rpcMethods[method])\r\n// return rpcMethods[method](payload);\r\n// else {\r\n// console.error(`Unknown rpc method ${method}`);\r\n// return 'error';\r\n// }\r\n// };\r\n\r\n\r\n// for debugging\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n// (window as any).SendMessage = SendMessage;\r\n// messageSubject.subscribe(console.log);\r\n","import { RegisterChannel, SendMessage, UnregisterChannel } from './Networking';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: { [channel: string]: { [ command: string ]: genericCallback[] } } = { };\r\n\r\nconst registerListener = <T>(channel: string, type: string, callback: (val: T) => void) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = { };\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach(cb => cb(msg.payload as T));\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (channel: string, callback: (val: boolean[]) => void) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (channel: string, callback: (val: number[]) => void) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (channel: string, callback: (val: [number, number, number]) => void) => {\r\n registerListener<[number, number, number]>(channel, 'broadcast::vector3', callback);\r\n};\r\n\r\nconst receiveVector3Array = (channel: string, callback: (val: [number, number, number][]) => void) => {\r\n registerListener<[number, number, number][]>(channel, 'broadcast::vector3[]', callback);\r\n};\r\n\r\nconst receiveQuaternion = (channel: string, callback: (val: [number, number, number, number]) => void) => {\r\n registerListener<[number, number, number, number]>(channel, 'broadcast::quaternion', callback);\r\n};\r\n\r\nconst receiveQuaternionArray = (channel: string, callback: (val: [number, number, number, number][]) => void) => {\r\n registerListener<[number, number, number, number][]>(channel, 'broadcast::quaternion[]', callback);\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (channel: string, callback: (val: { [key: string]: unknown }) => void) => {\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool, sendBoolArray,\r\n sendNumber, sendNumberArray,\r\n sendString, sendStringArray,\r\n sendVector3, sendVector3Array,\r\n sendQuaternion, sendQuaternionArray,\r\n sendColor, sendColorArray,\r\n sendJson,\r\n\r\n receiveBool, receiveBoolArray,\r\n receiveNumber, receiveNumberArray,\r\n receiveString, receiveStringArray,\r\n receiveVector3, receiveVector3Array,\r\n receiveQuaternion, receiveQuaternionArray,\r\n receiveColor, receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Networking';\r\n\r\nconst init = () => {\r\n // keep original console commands\r\n const consoleDebug = console.debug;\r\n const consoleLog = console.log;\r\n const consoleWarn = console.warn;\r\n const consoleError = console.error;\r\n\r\n const stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n return JSON.stringify(obj);\r\n };\r\n\r\n // intercept calls from console\r\n console.debug = function(...args) {\r\n consoleDebug.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'debug', [...args].map(stringify).join().trim());\r\n };\r\n console.log = function(...args) {\r\n consoleLog.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'info', [...args].map(stringify).join().trim());\r\n };\r\n console.warn = function(...args) {\r\n consoleWarn.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'warning', [...args].map(stringify).join().trim());\r\n };\r\n console.error = function(...args) {\r\n consoleError.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'error', [...args].map(stringify).join().trim());\r\n };\r\n};\r\n\r\n\r\nexport const RemoteLogger = {\r\n init\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Networking';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new(id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [ Observable<T[]>, (model: T) => void ];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(registration: ModelSyncRegistration<T>): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(`${name}`, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find(m => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', newModel.toJson(changes));\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find(m => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter(m => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [ models.asObservable(), registerModel ];\r\n};\r\n\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
1
+ {"version":3,"sources":["../src/Networking.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["init","Subject"],"mappings":"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","sourcesContent":["import { Subject } from 'rxjs';\r\nimport * as io from 'socket.io-client';\r\n\r\nexport interface Message {\r\n channel: string,\r\n command: string,\r\n payload: unknown\r\n}\r\n\r\nconst messageSubject = new Subject<Message>();\r\nexport const Messages = messageSubject.asObservable();\r\n\r\nlet location = '';\r\nif (typeof window !== 'undefined') {\r\n location = window.location.hostname || '';\r\n}\r\n\r\nconst socket = io.connect({\r\n autoConnect: false\r\n});\r\n\r\n\r\nconst init = (app: string, server?: string) => {\r\n server ??= location;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += ':9011';\r\n\r\n // FIXME: terrible workaround because socket.io marked uri as readonly\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (socket.io as any).uri = server;\r\n socket.io.opts.query = { app, version: '1' };\r\n socket.connect();\r\n\r\n console.debug(`Connecting to colibri server on ${server}`);\r\n socket.on('connect', () => {\r\n console.debug(`Connected to colibri server on ${server}`);\r\n });\r\n\r\n socket.onAny((channel, msg) => {\r\n // if (msg.command === 'rpcRequest') {\r\n // const result = CallRpc(msg.channel, msg.payload['parameters']);\r\n // socket.emit(msg.channel, {\r\n // group: APP_NAME,\r\n // command: 'rpcAnswer',\r\n // payload: {\r\n // result,\r\n // rpcId: msg.payload['rpcId']\r\n // }\r\n // });\r\n // }\r\n messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload\r\n });\r\n });\r\n};\r\n\r\nexport const Colibri = {\r\n init\r\n};\r\n\r\n\r\n///////////////////////////\r\n/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (channel: string, command: string, payload: unknown = {}) => {\r\n socket.emit(channel, {\r\n command,\r\n payload\r\n });\r\n};\r\n\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (channel: string, handler: (payload: Message) => void) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (channel: string, handler: (payload: Message) => void) => {\r\n socket.once(channel, handler);\r\n};\r\n\r\n\r\n//////////////////\r\n/// RPC\r\n//////////////////\r\n// type rpcMethod = (payload: unknown) => unknown;\r\n// const rpcMethods: {[method: string]: rpcMethod } = {};\r\n// export const RegisterRpc = (method: string, fn: rpcMethod): void => {\r\n// rpcMethods[method] = fn;\r\n// };\r\n\r\n// export const SendRpc = async (method: string, payload: any): Promise<any> => {\r\n// // TODO\r\n// return null;\r\n// };\r\n\r\n// const CallRpc = (method: string, payload: any): any => {\r\n// if (rpcMethods[method])\r\n// return rpcMethods[method](payload);\r\n// else {\r\n// console.error(`Unknown rpc method ${method}`);\r\n// return 'error';\r\n// }\r\n// };\r\n\r\n\r\n// for debugging\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n// (window as any).SendMessage = SendMessage;\r\n// messageSubject.subscribe(console.log);\r\n","import { RegisterChannel, SendMessage } from './Networking';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: { [channel: string]: { [ command: string ]: genericCallback[] } } = { };\r\n\r\nconst registerListener = <T>(channel: string, type: string, callback: (val: T) => void) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = { };\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach(cb => cb(msg.payload as T));\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (channel: string, callback: (val: boolean[]) => void) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (channel: string, callback: (val: number[]) => void) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (channel: string, callback: (val: [number, number, number]) => void) => {\r\n registerListener<[number, number, number]>(channel, 'broadcast::vector3', callback);\r\n};\r\n\r\nconst receiveVector3Array = (channel: string, callback: (val: [number, number, number][]) => void) => {\r\n registerListener<[number, number, number][]>(channel, 'broadcast::vector3[]', callback);\r\n};\r\n\r\nconst receiveQuaternion = (channel: string, callback: (val: [number, number, number, number]) => void) => {\r\n registerListener<[number, number, number, number]>(channel, 'broadcast::quaternion', callback);\r\n};\r\n\r\nconst receiveQuaternionArray = (channel: string, callback: (val: [number, number, number, number][]) => void) => {\r\n registerListener<[number, number, number, number][]>(channel, 'broadcast::quaternion[]', callback);\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (channel: string, callback: (val: { [key: string]: unknown }) => void) => {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool, sendBoolArray,\r\n sendNumber, sendNumberArray,\r\n sendString, sendStringArray,\r\n sendVector3, sendVector3Array,\r\n sendQuaternion, sendQuaternionArray,\r\n sendColor, sendColorArray,\r\n sendJson,\r\n\r\n receiveBool, receiveBoolArray,\r\n receiveNumber, receiveNumberArray,\r\n receiveString, receiveStringArray,\r\n receiveVector3, receiveVector3Array,\r\n receiveQuaternion, receiveQuaternionArray,\r\n receiveColor, receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Networking';\r\n\r\nconst init = () => {\r\n // keep original console commands\r\n const consoleDebug = console.debug;\r\n const consoleLog = console.log;\r\n const consoleWarn = console.warn;\r\n const consoleError = console.error;\r\n\r\n const stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n const cache: unknown[] = [];\r\n const str = JSON.stringify(obj, (_, value: unknown) => {\r\n if (typeof value === 'object' && value !== null) {\r\n if (cache.indexOf(value) !== -1) {\r\n // Circular reference found, discard key\r\n return;\r\n }\r\n // Store value in our collection\r\n cache.push(value);\r\n }\r\n return value;\r\n }, 2);\r\n return str;\r\n };\r\n\r\n // intercept calls from console\r\n console.debug = function (...args) {\r\n consoleDebug.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'debug', [...args].map(stringify).join().trim());\r\n };\r\n console.log = function (...args) {\r\n consoleLog.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'info', [...args].map(stringify).join().trim());\r\n };\r\n console.warn = function (...args) {\r\n consoleWarn.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'warning', [...args].map(stringify).join().trim());\r\n };\r\n console.error = function (...args) {\r\n consoleError.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'error', [...args].map(stringify).join().trim());\r\n };\r\n};\r\n\r\n\r\nexport const RemoteLogger = {\r\n init\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Networking';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new(id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [ Observable<T[]>, (model: T) => void ];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(registration: ModelSyncRegistration<T>): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(name, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find(m => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', newModel.toJson(changes));\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find(m => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter(m => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [ models.asObservable(), registerModel ];\r\n};\r\n\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hcikn/colibri",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Communication Library for Research Prototyping - Allows object synchronization, networking, and voice chat with little to no code required",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",