@4players/odin-plugin-web 1.0.0-alpha.2 → 1.0.0-alpha.20

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.
Files changed (95) hide show
  1. package/lib/common/msgpack.d.ts +2 -0
  2. package/lib/common/msgpack.js +117 -0
  3. package/{backend/lib → lib/plugin}/config.d.ts +5 -4
  4. package/lib/plugin/config.js +22 -0
  5. package/{backend/lib → lib/plugin}/connectionPool.d.ts +3 -3
  6. package/{backend/lib → lib/plugin}/connectionPool.js +32 -13
  7. package/lib/plugin/deviceManager.d.ts +12 -0
  8. package/lib/plugin/deviceManager.js +120 -0
  9. package/lib/plugin/h3/mainConnection.d.ts +31 -0
  10. package/lib/plugin/h3/mainConnection.js +195 -0
  11. package/{backend/lib → lib/plugin}/index.d.ts +30 -13
  12. package/lib/plugin/index.js +404 -0
  13. package/lib/plugin/mainConnection.d.ts +30 -0
  14. package/lib/plugin/mainConnection.js +21 -0
  15. package/{backend/lib/audioController.d.ts → lib/plugin/mediaController.d.ts} +12 -48
  16. package/lib/plugin/mediaController.js +215 -0
  17. package/lib/plugin/medias/audio-capture.d.ts +30 -0
  18. package/lib/plugin/medias/audio-capture.js +224 -0
  19. package/lib/plugin/medias/audio-playback.d.ts +13 -0
  20. package/lib/plugin/medias/audio-playback.js +40 -0
  21. package/lib/plugin/medias/video-capture.d.ts +15 -0
  22. package/lib/plugin/medias/video-capture.js +69 -0
  23. package/lib/plugin/medias/video-playback.d.ts +13 -0
  24. package/lib/plugin/medias/video-playback.js +20 -0
  25. package/{backend/lib → lib/plugin}/roomConnection.d.ts +9 -6
  26. package/{backend/lib → lib/plugin}/roomConnection.js +74 -53
  27. package/lib/plugin/rpcConnection.d.ts +57 -0
  28. package/{backend/lib → lib/plugin}/rpcConnection.js +129 -83
  29. package/lib/plugin/token.d.ts +13 -0
  30. package/{backend/lib → lib/plugin}/token.js +8 -9
  31. package/lib/plugin/webrtc/mainConnection.d.ts +53 -0
  32. package/lib/plugin/webrtc/mainConnection.js +455 -0
  33. package/lib/plugin/webrtc/websocket.d.ts +3 -0
  34. package/lib/plugin/webrtc/websocket.js +56 -0
  35. package/lib/worker/buffers.d.ts +42 -0
  36. package/lib/worker/buffers.js +79 -0
  37. package/{worker/lib/index.d.ts → lib/worker/common.d.ts} +7 -8
  38. package/lib/worker/common.js +7 -0
  39. package/lib/worker/config.d.ts +8 -0
  40. package/lib/worker/config.js +11 -0
  41. package/lib/worker/index.d.ts +3 -0
  42. package/lib/worker/index.js +1 -0
  43. package/lib/worker/memory.d.ts +9 -0
  44. package/lib/worker/memory.js +29 -0
  45. package/lib/worker/messages.d.ts +14 -0
  46. package/lib/worker/messages.js +15 -0
  47. package/lib/worker/packet.d.ts +11 -0
  48. package/lib/worker/packet.js +59 -0
  49. package/lib/worker/shared/capture.d.ts +27 -0
  50. package/lib/worker/shared/capture.js +63 -0
  51. package/lib/worker/shared/index.d.ts +3 -0
  52. package/lib/worker/shared/index.js +147 -0
  53. package/lib/worker/shared/messages.d.ts +67 -0
  54. package/lib/worker/shared/messages.js +64 -0
  55. package/lib/worker/shared/playback.d.ts +20 -0
  56. package/lib/worker/shared/playback.js +78 -0
  57. package/lib/worker/unshared/capture.d.ts +27 -0
  58. package/lib/worker/unshared/capture.js +66 -0
  59. package/lib/worker/unshared/index.d.ts +3 -0
  60. package/lib/worker/unshared/index.js +170 -0
  61. package/{worker/lib → lib/worker/unshared}/messages.d.ts +3 -15
  62. package/lib/worker/unshared/messages.js +80 -0
  63. package/lib/worker/unshared/playback.d.ts +22 -0
  64. package/lib/worker/unshared/playback.js +83 -0
  65. package/package.json +29 -26
  66. package/backend/lib/audioController.d.ts.map +0 -1
  67. package/backend/lib/audioController.js +0 -442
  68. package/backend/lib/audioController.js.map +0 -1
  69. package/backend/lib/config.d.ts.map +0 -1
  70. package/backend/lib/config.js +0 -11
  71. package/backend/lib/config.js.map +0 -1
  72. package/backend/lib/connectionPool.d.ts.map +0 -1
  73. package/backend/lib/connectionPool.js.map +0 -1
  74. package/backend/lib/index.d.ts.map +0 -1
  75. package/backend/lib/index.js +0 -293
  76. package/backend/lib/index.js.map +0 -1
  77. package/backend/lib/mainConnection.d.ts +0 -40
  78. package/backend/lib/mainConnection.d.ts.map +0 -1
  79. package/backend/lib/mainConnection.js +0 -294
  80. package/backend/lib/mainConnection.js.map +0 -1
  81. package/backend/lib/roomConnection.d.ts.map +0 -1
  82. package/backend/lib/roomConnection.js.map +0 -1
  83. package/backend/lib/rpcConnection.d.ts +0 -49
  84. package/backend/lib/rpcConnection.d.ts.map +0 -1
  85. package/backend/lib/rpcConnection.js.map +0 -1
  86. package/backend/lib/token.d.ts +0 -16
  87. package/backend/lib/token.d.ts.map +0 -1
  88. package/backend/lib/token.js.map +0 -1
  89. package/backend/package.json +0 -13
  90. package/wasm/lib/index.js +0 -19
  91. package/wasm/package.json +0 -14
  92. package/worker/lib/common.d.ts +0 -27
  93. package/worker/lib/index.js +0 -2
  94. package/worker/package.json +0 -13
  95. /package/{wasm/lib → lib/wasm}/index.d.ts +0 -0
@@ -8,55 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- import * as msgpack from '@msgpack/msgpack';
12
- import { failure, success, isKnownRpcMessage, parseRpc, parseRpcMessage, abortableSleep, } from '@4players/odin-common';
13
- import { ConnectInterval } from './config';
14
- export function connect(url, signal) {
15
- return __awaiter(this, void 0, void 0, function* () {
16
- console.debug(`connecting to ${url}`);
17
- const start = performance.now();
18
- let connection = failure('no connection was attempted');
19
- for (let failures = 0; failures < ConnectInterval.length && signal.aborted === false; failures++) {
20
- const nextReconnectAt = start + whenToConnect(failures);
21
- if ((yield abortableSleep(nextReconnectAt - performance.now(), signal)) ===
22
- 'aborted') {
23
- return failure('aborted');
24
- }
25
- connection = yield connectOnce(url, signal);
26
- if (connection.type === 'Success') {
27
- return connection;
28
- }
29
- }
30
- return connection;
31
- });
32
- }
33
- export function connectOnce(url, signal) {
34
- return __awaiter(this, void 0, void 0, function* () {
35
- let webSocket;
36
- try {
37
- webSocket = new WebSocket(url);
38
- }
39
- catch (reason) {
40
- return failure(`unable to create websocket; ${reason}`);
41
- }
42
- const abort = new AbortController();
43
- const onOpen = new Promise((accept) => webSocket.addEventListener('open', () => accept(success(webSocket)), {
44
- signal: abort.signal,
45
- }));
46
- const onClose = new Promise((accept) => webSocket.addEventListener('close', (event) => accept(failure(event.reason)), { signal: abort.signal }));
47
- const onAbort = new Promise((accept) => signal.addEventListener('abort', () => accept(failure('aborted')), {
48
- signal: abort.signal,
49
- }));
50
- const result = yield Promise.race([onOpen, onClose, onAbort]);
51
- abort.abort();
52
- return result;
53
- });
54
- }
55
- function whenToConnect(failures) {
56
- const past_connects = ConnectInterval.slice(0, failures).reduce((a, b) => a + b, 0);
57
- const interval = ConnectInterval[failures] * Math.random();
58
- return past_connects + interval;
59
- }
11
+ import { failure, success, msgpackEncode, msgpackDecode, MessagePackRpcSchema, } from '@4players/odin-common';
12
+ import { getByteSize } from '../common/msgpack';
60
13
  export class RpcConnection {
61
14
  get closed() {
62
15
  return this._Close.signal.aborted;
@@ -64,17 +17,26 @@ export class RpcConnection {
64
17
  get closeSignal() {
65
18
  return this._Close.signal;
66
19
  }
67
- constructor(webSocket) {
68
- this.webSocket = webSocket;
20
+ constructor(stream) {
21
+ this.stream = stream;
69
22
  this._SendQueue = [];
70
23
  this._RecvQueue = [];
24
+ this._EventQueue = [];
71
25
  this._Requests = new Map();
72
26
  this._Close = new AbortController();
73
27
  this._NextId = 1;
74
- this.webSocket.addEventListener('open', this._OnWebSocketOpen.bind(this));
75
- this.webSocket.addEventListener('close', this.close.bind(this));
76
- this.webSocket.addEventListener('message', this._OnWebSocketMessage.bind(this));
77
- this.webSocket.binaryType = 'arraybuffer';
28
+ if (stream instanceof WebSocket) {
29
+ stream.addEventListener('open', this._OnWebSocketOpen.bind(this));
30
+ stream.addEventListener('close', this.close.bind(this));
31
+ stream.addEventListener('message', (event) => this._OnRpcMessage(event.data));
32
+ stream.binaryType = 'arraybuffer';
33
+ }
34
+ else {
35
+ readRpcFromStream(stream.readable.getReader(), this._OnRpcMessage.bind(this))
36
+ .catch(console.error)
37
+ .finally(this.close.bind(this));
38
+ this._Writer = stream.writable.getWriter();
39
+ }
78
40
  this._Close.signal.addEventListener('abort', this._Cleanup.bind(this), {
79
41
  once: true,
80
42
  });
@@ -96,16 +58,20 @@ export class RpcConnection {
96
58
  });
97
59
  }
98
60
  notify(name, properties) {
99
- if (this.closed === false) {
61
+ if (!this.closed) {
100
62
  this._Send([2, name, properties]);
101
63
  }
102
64
  }
103
65
  recv() {
104
- if (this.closed === false) {
105
- return new Promise((accept) => this._RecvQueue.push(accept));
66
+ if (this.closed) {
67
+ return Promise.resolve('closed');
68
+ }
69
+ const event = this._EventQueue.shift();
70
+ if (event !== undefined) {
71
+ return Promise.resolve(event);
106
72
  }
107
73
  else {
108
- return Promise.resolve('closed');
74
+ return new Promise((accept) => this._RecvQueue.push(accept));
109
75
  }
110
76
  }
111
77
  close() {
@@ -120,18 +86,26 @@ export class RpcConnection {
120
86
  request.accept('timeout');
121
87
  }
122
88
  _Send(message) {
123
- this.webSocket.send(msgpack.encode(message));
89
+ const encoded = message instanceof Uint8Array
90
+ ? message
91
+ : new Uint8Array(msgpackEncode(message));
92
+ if (this.stream instanceof WebSocket) {
93
+ this.stream.send(encoded);
94
+ }
95
+ else {
96
+ this._Writer.write(encoded);
97
+ }
124
98
  }
125
99
  _OnWebSocketOpen(ev) {
126
- for (const data in this._SendQueue) {
127
- this.webSocket.send(data);
100
+ for (const data of this._SendQueue) {
101
+ this._Send(data);
128
102
  }
129
103
  this._SendQueue.length = 0;
130
104
  }
131
- _OnWebSocketMessage(ev) {
132
- const rpc = parseRpc(ev.data);
105
+ _OnRpcMessage(bytes) {
106
+ const rpc = parseRpc(bytes);
133
107
  if (rpc.type !== 'Success') {
134
- this.webSocket.close();
108
+ this.close();
135
109
  return;
136
110
  }
137
111
  switch (rpc.value[0]) {
@@ -154,14 +128,16 @@ export class RpcConnection {
154
128
  request.accept(error === null ? success(result) : failure(error));
155
129
  }
156
130
  _OnEvent(name, properties) {
157
- console.log('event', name, properties);
158
131
  const receiver = this._RecvQueue.shift();
159
132
  if (receiver !== undefined) {
160
133
  receiver({ name, properties });
161
134
  }
135
+ else {
136
+ this._EventQueue.push({ name, properties });
137
+ }
162
138
  }
163
139
  _Cleanup() {
164
- console.log(`cleanup ${this.webSocket.url}`);
140
+ var _a;
165
141
  for (const request of this._Requests.values()) {
166
142
  clearTimeout(request.timeoutId);
167
143
  request.accept('closed');
@@ -172,24 +148,34 @@ export class RpcConnection {
172
148
  }
173
149
  this._RecvQueue.length = 0;
174
150
  this._SendQueue.length = 0;
175
- this.webSocket.close();
151
+ if (this.stream instanceof WebSocket) {
152
+ this.stream.close();
153
+ }
154
+ else {
155
+ (_a = this._Writer) === null || _a === void 0 ? void 0 : _a.close().catch(() => {
156
+ /* drop silently */
157
+ });
158
+ }
176
159
  }
177
160
  }
178
- export class TypedRpcConnection extends RpcConnection {
179
- constructor(webSocket, _Commands, _Events) {
180
- super(webSocket);
161
+ export class TypedRpcConnection {
162
+ get closed() {
163
+ return this.rpcConnection.closed;
164
+ }
165
+ get closeSignal() {
166
+ return this.rpcConnection.closeSignal;
167
+ }
168
+ constructor(rpcConnection, _Commands, _Events) {
169
+ this.rpcConnection = rpcConnection;
181
170
  this._Commands = _Commands;
182
171
  this._Events = _Events;
183
172
  }
184
173
  request(name, properties, timeout) {
185
- const _super = Object.create(null, {
186
- request: { get: () => super.request }
187
- });
188
174
  return __awaiter(this, void 0, void 0, function* () {
189
175
  properties = this._Commands[name].request.parse(properties);
190
176
  const response = yield (timeout !== undefined
191
- ? _super.request.call(this, name, properties, timeout)
192
- : _super.request.call(this, name, properties));
177
+ ? this.rpcConnection.request(name, properties, timeout)
178
+ : this.rpcConnection.request(name, properties));
193
179
  if (response === 'closed' ||
194
180
  response === 'timeout' ||
195
181
  response.type === 'Failure') {
@@ -207,14 +193,11 @@ export class TypedRpcConnection extends RpcConnection {
207
193
  }
208
194
  notify(name, properties) {
209
195
  properties = this._Commands[name].request.parse(properties);
210
- super.notify(name, properties);
196
+ this.rpcConnection.notify(name, properties);
211
197
  }
212
198
  recv() {
213
- const _super = Object.create(null, {
214
- recv: { get: () => super.recv }
215
- });
216
199
  return __awaiter(this, void 0, void 0, function* () {
217
- const event = yield _super.recv.call(this);
200
+ const event = yield this.rpcConnection.recv();
218
201
  if (event === 'closed')
219
202
  return event;
220
203
  const parsed = parseRpcMessage(this._Events, event);
@@ -226,10 +209,73 @@ export class TypedRpcConnection extends RpcConnection {
226
209
  }
227
210
  else {
228
211
  console.error(`schema validation for ${event.name} failed; closing connection`);
229
- this.close();
212
+ this.rpcConnection.close();
230
213
  }
231
214
  return 'closed';
232
215
  });
233
216
  }
217
+ close() {
218
+ this.rpcConnection.close();
219
+ }
220
+ }
221
+ export function parseRpc(buffer) {
222
+ try {
223
+ return success(MessagePackRpcSchema.parse(msgpackDecode(buffer)));
224
+ }
225
+ catch (error) {
226
+ return failure(String(error));
227
+ }
228
+ }
229
+ export function parseRpcMessage(events, rpc) {
230
+ if (!isKnownRpcMessage(events, rpc.name))
231
+ return undefined;
232
+ const parsed = events[rpc.name].safeParse(rpc.properties);
233
+ if (!parsed.success) {
234
+ return undefined;
235
+ }
236
+ return {
237
+ name: rpc.name,
238
+ properties: parsed.data,
239
+ };
240
+ }
241
+ export function isKnownRpcMessage(events, name) {
242
+ return name in events;
243
+ }
244
+ function readRpcFromStream(reader, onRpc) {
245
+ return __awaiter(this, void 0, void 0, function* () {
246
+ let buffer = new Uint8Array();
247
+ for (;;) {
248
+ let read;
249
+ try {
250
+ read = yield reader.read();
251
+ }
252
+ catch (error) {
253
+ break;
254
+ }
255
+ if (read.done)
256
+ break;
257
+ buffer = mergeBuffer(buffer, read.value);
258
+ let offset = 0;
259
+ for (let size;; offset += size) {
260
+ size = getByteSize(buffer, offset);
261
+ if (!size || offset + buffer.length < size)
262
+ break;
263
+ onRpc(buffer.subarray(offset, offset + size));
264
+ }
265
+ buffer = buffer.subarray(offset);
266
+ }
267
+ if (buffer.length > 0) {
268
+ console.warn('unprocessed bytes left in rpc stream', buffer);
269
+ }
270
+ });
271
+ }
272
+ function mergeBuffer(a, b) {
273
+ if (a.length === 0)
274
+ return b;
275
+ if (b.length === 0)
276
+ return a;
277
+ const merged = new Uint8Array(a.length + b.length);
278
+ merged.set(a, 0);
279
+ merged.set(b, a.length);
280
+ return merged;
234
281
  }
235
- //# sourceMappingURL=rpcConnection.js.map
@@ -0,0 +1,13 @@
1
+ import { TokenAudienceSchema, TokenClaims, Result, Infer } from '@4players/odin-common';
2
+ export declare class Token {
3
+ readonly text: string;
4
+ readonly claims: TokenClaims;
5
+ static parse(token: string): Result<Token>;
6
+ private readonly _Upstream;
7
+ protected constructor(text: string, claims: TokenClaims);
8
+ get timeLeft(): number;
9
+ get upstream(): Token | undefined;
10
+ get defaultRoomId(): string | undefined;
11
+ hasAudience(audience: Infer<typeof TokenAudienceSchema>): boolean;
12
+ toString(): string;
13
+ }
@@ -24,13 +24,13 @@ export class Token {
24
24
  constructor(text, claims) {
25
25
  this.text = text;
26
26
  this.claims = claims;
27
- const address = claims.adr;
28
- if (address !== undefined) {
29
- try {
30
- this._Address = new URL(`wss://${address}`);
27
+ if (claims.ups !== undefined) {
28
+ const upstream = Token.parse(claims.ups);
29
+ if (upstream.type === 'Success') {
30
+ this._Upstream = upstream.value;
31
31
  }
32
- catch (error) {
33
- console.error(`address in token(${address}) can not be parsed; ${error}`);
32
+ else {
33
+ console.error(`upstream in token can not be parsed; ${upstream.reason}`);
34
34
  }
35
35
  }
36
36
  }
@@ -39,8 +39,8 @@ export class Token {
39
39
  return Infinity;
40
40
  return this.claims.exp * 1000 - Date.now();
41
41
  }
42
- get address() {
43
- return this._Address;
42
+ get upstream() {
43
+ return this._Upstream;
44
44
  }
45
45
  get defaultRoomId() {
46
46
  if (Array.isArray(this.claims.rid)) {
@@ -64,4 +64,3 @@ export class Token {
64
64
  return this.text;
65
65
  }
66
66
  }
67
- //# sourceMappingURL=token.js.map
@@ -0,0 +1,53 @@
1
+ import { MainCommands, MainNotifications, Result } from '@4players/odin-common';
2
+ import { type MainConnection, type DatagramHandler, ConnectionStatus } from '../mainConnection';
3
+ import { Token } from '../token';
4
+ import { RpcConnection, TypedRpcConnection } from '../rpcConnection';
5
+ import { Transport } from '../index';
6
+ export declare class WebRtcMainConnection implements MainConnection {
7
+ readonly id: number;
8
+ readonly address: string;
9
+ readonly url: URL;
10
+ readonly onDatagram: DatagramHandler;
11
+ readonly connection: TypedRpcConnection<MainCommands, MainNotifications>;
12
+ readonly webRtc: RTCPeerConnection;
13
+ static create(id: number, address: string, onDatagram: DatagramHandler, signal: AbortSignal): Promise<Result<MainConnection>>;
14
+ private readonly _AudioChannel;
15
+ private readonly _PeerIdMap;
16
+ private _PeriodicHealthCheck;
17
+ private _WebRtcIceTimeout;
18
+ private _WebRtcTimeout;
19
+ private _ClosePromise;
20
+ private _LocalVideos;
21
+ private _RemoteVideos;
22
+ private _Negotiation;
23
+ private _ResolveOffer?;
24
+ private _awaitingRemoteTracks;
25
+ get transport(): Transport;
26
+ get closePromise(): Promise<"closed">;
27
+ get closeSignal(): AbortSignal;
28
+ get closed(): boolean;
29
+ get status(): ConnectionStatus;
30
+ waitForConnected(): Promise<void>;
31
+ private constructor();
32
+ close(): void;
33
+ joinRoom(token: Token, roomId: string, userData: Uint8Array, position: [number, number, number]): Promise<Result<RpcConnection> | 'closed'>;
34
+ getReconnectToken(peerId: number): Promise<Result<Token>>;
35
+ sendDatagram(datagram: Uint8Array): void;
36
+ healthCheck(): Promise<boolean>;
37
+ private _Request;
38
+ private _readWebRtcUpdate;
39
+ private _SendOffer;
40
+ private _OnWebRtcStatusChanged;
41
+ private _OnWebRtcUpdate;
42
+ private _UpdateRemoteSdp;
43
+ private _AddIceCandidate;
44
+ private _OnIceCandidate;
45
+ private ensureTransceivers;
46
+ startLocalVideo(mediaId: number, track: MediaStreamTrack): Promise<void>;
47
+ stopLocalVideo(mediaId: number): void;
48
+ startRemoteVideo(mediaId: number): Promise<MediaStreamTrack>;
49
+ stopRemoteVideo(mediaId: number): void;
50
+ private onTrack;
51
+ requestNegotiation(waitForAnswer: boolean): void;
52
+ private _CleanUp;
53
+ }