@hcikn/colibri 1.0.4 → 1.1.1

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
@@ -8,9 +8,13 @@
8
8
  ## Configuration
9
9
 
10
10
  Initialize Colibri once with:
11
+
11
12
  ```ts
12
13
  import { Colibri } from '@hcikn/colibri';
13
14
  Colibri.init('app_name', 'server_address');
15
+
16
+ // if using a custom port:
17
+ Colibri.init('app_name', 'server_address', 9011);
14
18
  ```
15
19
 
16
20
  For server setup, refer to [colibri-server](../colibri-server/).
@@ -34,12 +38,14 @@ See also [the remote-logging sample](samples/remote-logging.ts) (run sample with
34
38
 
35
39
  Colibri supports simple data transmission via pub/sub communication. Data can be published from anywhere in
36
40
  the executed code, as illustrated with the following simple example of sending a boolean value on a "click" channel:
41
+
37
42
  ```ts
38
43
  import { Sync } from '@hcikn/colibri';
39
44
  Sync.sendBool('myChannel', true);
40
45
  ```
41
46
 
42
47
  The sent data can then be received anywhere by registering a listener:
48
+
43
49
  ```ts
44
50
  Sync.receiveBool('myChannel', (value) => {
45
51
  // Will be called whenever a bool on "click" channel is received
@@ -47,11 +53,12 @@ Sync.receiveBool('myChannel', (value) => {
47
53
  ```
48
54
 
49
55
  The listener can be deregistered via:
56
+
50
57
  ```ts
51
58
  Sync.unregister('myChannel', MyMethod);
52
59
  ```
53
60
 
54
- The following built-in types are available for sync: `bool, int (as number), float (as number), string, Vector2, Vector3, Quaternion, Color` and arrays thereof. For arbitrary data, you can use JSON:
61
+ The following built-in types are available for sync: `bool, int (as number), float (as number), string, Vector2, Vector3, Quaternion, Color` and arrays thereof. For arbitrary data, you can use JSON:
55
62
 
56
63
  ```c#
57
64
  Sync.sendJson('myChannel', { foo: 'bar' });
@@ -61,14 +68,16 @@ Sync.receiveJson('myChannel', (json) => { /* ... */ });
61
68
  See also [the broadcast sample](samples/broadcast.ts) (run sample with `npm run sample/broadcast`).
62
69
 
63
70
  Limitations:
64
- - You have to register the listener *before* sending out data
65
- - Type and channel *must* match between Listener and Sender (`number` will be converted to float for Unity clients, i.e., use `float` listener on Unity clients for sending numbers!)
71
+
72
+ - You have to register the listener _before_ sending out data
73
+ - Type and channel _must_ match between Listener and Sender (`number` will be converted to float for Unity clients, i.e., use `float` listener on Unity clients for sending numbers!)
66
74
  - Remember to unregister your listener where necessary!
67
75
 
68
76
  ### SyncModel
77
+
69
78
  (Counterpart to [SyncBehaviour in Unity client](../colibri-unity#SyncBehaviour))
70
79
 
71
- For more complex scenarios, Colibri supports synchronization of data models (e.g., for use in model-view-controller architectures). For this, we need extend the *Model* with `SyncModel`:
80
+ For more complex scenarios, Colibri supports synchronization of data models (e.g., for use in model-view-controller architectures). For this, we need extend the _Model_ with `SyncModel`:
72
81
 
73
82
  ```ts
74
83
  import { SyncModel, Synced } from '@hcikn/colibri';
@@ -88,6 +97,7 @@ export class SampleClass extends SyncModel<SampleClass> {
88
97
  ```
89
98
 
90
99
  and enable `experimentalDecorators` in the `tsconfig.json`:
100
+
91
101
  ```json
92
102
  {
93
103
  "compilerOptions": {
@@ -100,10 +110,12 @@ and enable `experimentalDecorators` in the `tsconfig.json`:
100
110
  Any property or field marked with `@Synced()` will be synchronized across all network clients. *Note: Synchronization of fields (e.g., `@Synced() private age = 0;` does not work on some frameworks such as React for some reason.*
101
111
 
102
112
  Lastly, we need to register the class with the Synchronization mechanism by calling `RegisterModelSync`:
113
+
103
114
  ```ts
104
115
  import { RegisterModelSync } from '@hcikn/colibri';
105
116
  const [ SampleClasses$, registerExampleClass ] = RegisterModelSync<SampleClass>({ type: SampleClass });
106
117
  ```
118
+
107
119
  The first return value (e.g., `SampleClasses$`) is a [BehaviorSubject](https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject) that will be updated whenever a new instance of SampleClass is added, updated, or deleted. The second return value (e.g., `registerExampleClass`) can be used to sync new instantiations:
108
120
 
109
121
  ```ts
@@ -114,4 +126,5 @@ registerExampleClass(mySample); // mySample is sent out to all other clients and
114
126
  See also [the model-sync sample](samples/model-sync.ts) (run sample with `npm run sample/model-sync`).
115
127
 
116
128
  ## Samples
129
+
117
130
  See [Sample folder](samples/) for more examples on how to use the Colibri web client.
package/dist/index.cjs CHANGED
@@ -143,12 +143,13 @@ if (typeof window !== "undefined") {
143
143
  var socket = io.connect({
144
144
  autoConnect: false
145
145
  });
146
- var init = function(app, server) {
146
+ var init = function(app, server, port) {
147
147
  server !== null && server !== void 0 ? server : server = location;
148
+ port !== null && port !== void 0 ? port : port = 9011;
148
149
  if (!server.startsWith("http")) {
149
150
  server = "http://" + server;
150
151
  }
151
- server += ":9011";
152
+ server += ":".concat(port);
152
153
  socket.io.uri = server;
153
154
  socket.io.opts.query = {
154
155
  app: app,
@@ -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\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"]}
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\nconst init = (app: string, server?: string, port?: number) => {\r\n server ??= location;\r\n port ??= 9011;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += `:${port}`;\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/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (\r\n channel: string,\r\n command: string,\r\n payload: unknown = {}\r\n) => {\r\n socket.emit(channel, {\r\n command,\r\n payload,\r\n });\r\n};\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.once(channel, handler);\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// 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.d.cts CHANGED
@@ -2,7 +2,7 @@ import * as rxjs from 'rxjs';
2
2
  import { Subject, Observable } from 'rxjs';
3
3
 
4
4
  declare const Colibri: {
5
- init: (app: string, server?: string) => void;
5
+ init: (app: string, server?: string, port?: number) => void;
6
6
  };
7
7
 
8
8
  type genericCallback = (val: any) => void;
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as rxjs from 'rxjs';
2
2
  import { Subject, Observable } from 'rxjs';
3
3
 
4
4
  declare const Colibri: {
5
- init: (app: string, server?: string) => void;
5
+ init: (app: string, server?: string, port?: number) => void;
6
6
  };
7
7
 
8
8
  type genericCallback = (val: any) => void;
package/dist/index.js CHANGED
@@ -61,12 +61,13 @@ if (typeof window !== "undefined") {
61
61
  var socket = io.connect({
62
62
  autoConnect: false
63
63
  });
64
- var init = function(app, server) {
64
+ var init = function(app, server, port) {
65
65
  server !== null && server !== void 0 ? server : server = location;
66
+ port !== null && port !== void 0 ? port : port = 9011;
66
67
  if (!server.startsWith("http")) {
67
68
  server = "http://" + server;
68
69
  }
69
- server += ":9011";
70
+ server += ":".concat(port);
70
71
  socket.io.uri = server;
71
72
  socket.io.opts.query = {
72
73
  app: app,
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\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"]}
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\nconst init = (app: string, server?: string, port?: number) => {\r\n server ??= location;\r\n port ??= 9011;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += `:${port}`;\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/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (\r\n channel: string,\r\n command: string,\r\n payload: unknown = {}\r\n) => {\r\n socket.emit(channel, {\r\n command,\r\n payload,\r\n });\r\n};\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.once(channel, handler);\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// 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.4",
3
+ "version": "1.1.1",
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",