@4players/odin-plugin-web 1.0.0-alpha.23 → 1.0.0-alpha.24
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/lib/plugin/index.d.ts +127 -61
- package/lib/plugin/index.js +1 -405
- package/lib/worker/shared.js +1 -0
- package/lib/worker/unshared.js +1 -0
- package/package.json +22 -21
- package/lib/common/msgpack.d.ts +0 -2
- package/lib/common/msgpack.js +0 -117
- package/lib/plugin/config.d.ts +0 -12
- package/lib/plugin/config.js +0 -22
- package/lib/plugin/connectionPool.d.ts +0 -17
- package/lib/plugin/connectionPool.js +0 -130
- package/lib/plugin/deviceManager.d.ts +0 -12
- package/lib/plugin/deviceManager.js +0 -120
- package/lib/plugin/h3/mainConnection.d.ts +0 -30
- package/lib/plugin/h3/mainConnection.js +0 -196
- package/lib/plugin/mainConnection.d.ts +0 -30
- package/lib/plugin/mainConnection.js +0 -23
- package/lib/plugin/mediaController.d.ts +0 -66
- package/lib/plugin/mediaController.js +0 -215
- package/lib/plugin/medias/audio-capture.d.ts +0 -30
- package/lib/plugin/medias/audio-capture.js +0 -224
- package/lib/plugin/medias/audio-playback.d.ts +0 -13
- package/lib/plugin/medias/audio-playback.js +0 -40
- package/lib/plugin/medias/video-capture.d.ts +0 -15
- package/lib/plugin/medias/video-capture.js +0 -69
- package/lib/plugin/medias/video-playback.d.ts +0 -13
- package/lib/plugin/medias/video-playback.js +0 -20
- package/lib/plugin/roomConnection.d.ts +0 -40
- package/lib/plugin/roomConnection.js +0 -320
- package/lib/plugin/rpcConnection.d.ts +0 -57
- package/lib/plugin/rpcConnection.js +0 -281
- package/lib/plugin/token.d.ts +0 -13
- package/lib/plugin/token.js +0 -66
- package/lib/plugin/webrtc/mainConnection.d.ts +0 -52
- package/lib/plugin/webrtc/mainConnection.js +0 -455
- package/lib/plugin/webrtc/websocket.d.ts +0 -3
- package/lib/plugin/webrtc/websocket.js +0 -56
- package/lib/version.d.ts +0 -1
- package/lib/version.js +0 -2
- package/lib/wasm/index.d.ts +0 -97
- package/lib/worker/buffers.d.ts +0 -42
- package/lib/worker/buffers.js +0 -79
- package/lib/worker/common.d.ts +0 -45
- package/lib/worker/common.js +0 -7
- package/lib/worker/config.d.ts +0 -8
- package/lib/worker/config.js +0 -11
- package/lib/worker/index.d.ts +0 -3
- package/lib/worker/index.js +0 -1
- package/lib/worker/memory.d.ts +0 -9
- package/lib/worker/memory.js +0 -29
- package/lib/worker/messages.d.ts +0 -14
- package/lib/worker/messages.js +0 -15
- package/lib/worker/packet.d.ts +0 -11
- package/lib/worker/packet.js +0 -59
- package/lib/worker/shared/capture.d.ts +0 -27
- package/lib/worker/shared/capture.js +0 -63
- package/lib/worker/shared/index.d.ts +0 -3
- package/lib/worker/shared/index.js +0 -147
- package/lib/worker/shared/messages.d.ts +0 -67
- package/lib/worker/shared/messages.js +0 -64
- package/lib/worker/shared/playback.d.ts +0 -20
- package/lib/worker/shared/playback.js +0 -78
- package/lib/worker/unshared/capture.d.ts +0 -27
- package/lib/worker/unshared/capture.js +0 -66
- package/lib/worker/unshared/index.d.ts +0 -3
- package/lib/worker/unshared/index.js +0 -170
- package/lib/worker/unshared/messages.d.ts +0 -81
- package/lib/worker/unshared/messages.js +0 -80
- package/lib/worker/unshared/playback.d.ts +0 -22
- package/lib/worker/unshared/playback.js +0 -83
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { Selector, failure, sleep, success, RoomCommandsRpc, RoomNotificationsRpc, } from '@4players/odin-common';
|
|
11
|
-
import { Token } from './token';
|
|
12
|
-
import { ConnectTimeout, logger, MinTokenLifetime, MinTokenRefreshInterval, } from './config';
|
|
13
|
-
export class RoomConnection {
|
|
14
|
-
get token() {
|
|
15
|
-
return this._Token;
|
|
16
|
-
}
|
|
17
|
-
get userData() {
|
|
18
|
-
return this._UserData;
|
|
19
|
-
}
|
|
20
|
-
get position() {
|
|
21
|
-
return this._Position;
|
|
22
|
-
}
|
|
23
|
-
get closed() {
|
|
24
|
-
return this._Close.signal.aborted;
|
|
25
|
-
}
|
|
26
|
-
get mainConnection() {
|
|
27
|
-
return this._MainConnection;
|
|
28
|
-
}
|
|
29
|
-
constructor(connectionPool, transport, onEvent, gateway, token, roomId, userData, position) {
|
|
30
|
-
this.connectionPool = connectionPool;
|
|
31
|
-
this.transport = transport;
|
|
32
|
-
this.onEvent = onEvent;
|
|
33
|
-
this.gateway = gateway;
|
|
34
|
-
this.roomId = roomId;
|
|
35
|
-
this._Close = new AbortController();
|
|
36
|
-
this._ConnectionFailures = 0;
|
|
37
|
-
this._FailureLimit = 5;
|
|
38
|
-
this._LastTokenRenew = performance.now();
|
|
39
|
-
this._Closed = new Promise((accept) => this._Close.signal.addEventListener('abort', () => accept('closed'), {
|
|
40
|
-
once: true,
|
|
41
|
-
}));
|
|
42
|
-
this._Token = token;
|
|
43
|
-
this._UserData = userData;
|
|
44
|
-
this._Position = position;
|
|
45
|
-
this._Main().then(({ reason }) => logger.info(['Connection'], `RoomConnection: Main() resolved; ${reason}`));
|
|
46
|
-
}
|
|
47
|
-
request(name, properties) {
|
|
48
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
if (this._RoomConnection === undefined) {
|
|
50
|
-
return failure('currently not connected');
|
|
51
|
-
}
|
|
52
|
-
const response = yield this._RoomConnection.request(name, properties);
|
|
53
|
-
if (response !== 'closed' && response.type === 'Success') {
|
|
54
|
-
this._HandleRequests(name, properties, response.value);
|
|
55
|
-
}
|
|
56
|
-
return response;
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
typedRequest(name, properties) {
|
|
60
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
-
const schema = RoomCommandsRpc[name];
|
|
62
|
-
const response = yield this.request(name, properties);
|
|
63
|
-
if (response === 'closed' || response.type === 'Failure')
|
|
64
|
-
return response;
|
|
65
|
-
const parsed = schema.response.safeParse(response.value);
|
|
66
|
-
if (parsed.success)
|
|
67
|
-
return success(parsed.data);
|
|
68
|
-
else {
|
|
69
|
-
return failure(`received invalid response from ${name}; ${parsed.error}`);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
close() {
|
|
74
|
-
this._Close.abort();
|
|
75
|
-
}
|
|
76
|
-
get renewTokenIn() {
|
|
77
|
-
const earliest = this._LastTokenRenew - performance.now() + MinTokenRefreshInterval;
|
|
78
|
-
const latest = this._Token.timeLeft - MinTokenLifetime;
|
|
79
|
-
return Math.max(earliest, latest);
|
|
80
|
-
}
|
|
81
|
-
_Main() {
|
|
82
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
-
var _a;
|
|
84
|
-
let closeReason = 'unable to establish connection';
|
|
85
|
-
/// as long as we are asked to continue
|
|
86
|
-
while (this._Close.signal.aborted === false) {
|
|
87
|
-
logger.debug(['Connection', 'RoomConnection._Main() loop']);
|
|
88
|
-
const result = yield this._ConnectToServer();
|
|
89
|
-
if (result.type === 'Failure') {
|
|
90
|
-
closeReason = result.reason;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
(_a = this._RoomConnection) === null || _a === void 0 ? void 0 : _a.close();
|
|
94
|
-
this._RoomConnection = undefined;
|
|
95
|
-
this._MainConnection = undefined;
|
|
96
|
-
this._OwnPeerId = undefined;
|
|
97
|
-
// can the token be used to request a better token from a gateway?
|
|
98
|
-
if (this.gateway !== undefined && this._Token.hasAudience('gateway')) {
|
|
99
|
-
this._ChangeStatus('Joining', `requesting new token from ${this.gateway}`);
|
|
100
|
-
const newToken = yield contactGateway(this.gateway, this._Token, this._Close.signal);
|
|
101
|
-
if (newToken.type === 'Success') {
|
|
102
|
-
if (newToken.value !== 'rejected') {
|
|
103
|
-
// we got a better token, lets retry.
|
|
104
|
-
this._Token = newToken.value;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
closeReason = 'invalid or expired token';
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// we failed to get a connection, should we try again?
|
|
114
|
-
if (this._Token.timeLeft <= 0) {
|
|
115
|
-
closeReason = 'unable to use expired token';
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
// slow retries down
|
|
119
|
-
yield sleep(1000);
|
|
120
|
-
}
|
|
121
|
-
this._ChangeStatus('Closed', this._Close.signal.aborted ? 'user requested' : closeReason);
|
|
122
|
-
this.close();
|
|
123
|
-
return failure(closeReason);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
_ConnectToServer() {
|
|
127
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
-
// for now strip upstream wrappers in the token, because it not supported in web
|
|
129
|
-
let token = this._Token;
|
|
130
|
-
while (token.upstream !== undefined) {
|
|
131
|
-
token = token.upstream;
|
|
132
|
-
}
|
|
133
|
-
if (!token.hasAudience('sfu') || token.claims.adr === undefined) {
|
|
134
|
-
return success('continue');
|
|
135
|
-
}
|
|
136
|
-
this._ChangeStatus('Joining', `connecting to ${token.claims.adr}`);
|
|
137
|
-
// are we already connected to the server?
|
|
138
|
-
if (this._MainConnection === undefined ||
|
|
139
|
-
this._MainConnection.closed ||
|
|
140
|
-
this._MainConnection.address !== token.claims.adr) {
|
|
141
|
-
// no? release the not fitting connection
|
|
142
|
-
if (this._MainConnection) {
|
|
143
|
-
this.connectionPool.releaseConnection(this._MainConnection);
|
|
144
|
-
}
|
|
145
|
-
// connect to the right server
|
|
146
|
-
const mainConnection = yield this.connectionPool.acquireConnection(this.transport, token.claims.adr, this._Close.signal);
|
|
147
|
-
if (mainConnection === 'closed')
|
|
148
|
-
return failure('connection pool closed'); // connection pool is closed
|
|
149
|
-
if (mainConnection.type === 'Failure') {
|
|
150
|
-
if (this._ConnectionFailures < this._FailureLimit) {
|
|
151
|
-
this._ConnectionFailures++;
|
|
152
|
-
return failure(`Connection failed; ${mainConnection.reason}`);
|
|
153
|
-
}
|
|
154
|
-
this._ChangeStatus('Joining', `connection failed; ${mainConnection.reason}`);
|
|
155
|
-
return success('continue');
|
|
156
|
-
}
|
|
157
|
-
this._MainConnection = mainConnection.value;
|
|
158
|
-
this._OwnPeerId = undefined;
|
|
159
|
-
}
|
|
160
|
-
// join room
|
|
161
|
-
const result = yield Promise.race([
|
|
162
|
-
Promise.all([
|
|
163
|
-
this._MainConnection.joinRoom(token, this.roomId, this.userData, this.position),
|
|
164
|
-
]),
|
|
165
|
-
sleep(ConnectTimeout, 'timeout'),
|
|
166
|
-
this._Closed,
|
|
167
|
-
]);
|
|
168
|
-
if (result === 'closed')
|
|
169
|
-
return failure('room connection closed'); // room connection was closed
|
|
170
|
-
if (result !== 'timeout') {
|
|
171
|
-
const [rpc] = result;
|
|
172
|
-
if (rpc === 'closed')
|
|
173
|
-
return success('continue'); // connection was lost, before joinRoom finished
|
|
174
|
-
if (rpc.type === 'Failure') {
|
|
175
|
-
// join was rejected by the server
|
|
176
|
-
return failure(`unable to join room; ${rpc.reason}`);
|
|
177
|
-
}
|
|
178
|
-
// room connection is open, make it available
|
|
179
|
-
this._RoomConnection = rpc.value;
|
|
180
|
-
this._ChangeStatus('Joined', `joined ${this.roomId}`);
|
|
181
|
-
// TODO(sven): handle this
|
|
182
|
-
this._MainConnection
|
|
183
|
-
.waitForConnected()
|
|
184
|
-
.catch((error) => console.error('main connection failed to connect', error));
|
|
185
|
-
// Handle the open connection until it gets closed
|
|
186
|
-
yield this._HandleConnection(this._MainConnection);
|
|
187
|
-
}
|
|
188
|
-
// try to reconnect
|
|
189
|
-
return success('continue');
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
_ChangeStatus(status, message) {
|
|
193
|
-
const properties = {
|
|
194
|
-
status,
|
|
195
|
-
message,
|
|
196
|
-
};
|
|
197
|
-
this.onEvent('RoomStatusChanged', properties);
|
|
198
|
-
}
|
|
199
|
-
_HandleConnection(main) {
|
|
200
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
-
var _a;
|
|
202
|
-
if (this._RoomConnection === undefined)
|
|
203
|
-
return;
|
|
204
|
-
const selector = new Selector([
|
|
205
|
-
() => sleep(this.renewTokenIn, 'renewToken'),
|
|
206
|
-
() => sleep(500, 'tick'),
|
|
207
|
-
() => __awaiter(this, void 0, void 0, function* () { var _a; return yield ((_a = this._RoomConnection) === null || _a === void 0 ? void 0 : _a.recv()); }),
|
|
208
|
-
() => this._Closed,
|
|
209
|
-
() => main.closePromise,
|
|
210
|
-
]);
|
|
211
|
-
while (true) {
|
|
212
|
-
const value = yield selector.next();
|
|
213
|
-
switch (value) {
|
|
214
|
-
case undefined:
|
|
215
|
-
case 'closed':
|
|
216
|
-
logger.debug(['Connection'], 'RoomConnection._HandleConnection: closed');
|
|
217
|
-
(_a = this._RoomConnection) === null || _a === void 0 ? void 0 : _a.close();
|
|
218
|
-
this.connectionPool.releaseConnection(main);
|
|
219
|
-
return;
|
|
220
|
-
case 'renewToken':
|
|
221
|
-
this._RenewToken();
|
|
222
|
-
break;
|
|
223
|
-
case 'tick':
|
|
224
|
-
break;
|
|
225
|
-
default:
|
|
226
|
-
this._HandleEvent(value);
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
_HandleEvent(event) {
|
|
233
|
-
if (event.name === 'RoomUpdated') {
|
|
234
|
-
const roomUpdated = RoomNotificationsRpc.RoomUpdated.safeParse(event.properties);
|
|
235
|
-
if (!roomUpdated.success)
|
|
236
|
-
return;
|
|
237
|
-
for (const update of roomUpdated.data.updates) {
|
|
238
|
-
switch (update.kind) {
|
|
239
|
-
case 'Joined':
|
|
240
|
-
this._OwnPeerId = update.own_peer_id;
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
this.onEvent(event.name, event.properties);
|
|
246
|
-
}
|
|
247
|
-
_HandleRequests(name, properties, response) {
|
|
248
|
-
switch (name) {
|
|
249
|
-
case 'SetPeerPosition': {
|
|
250
|
-
const setPeerPosition = RoomCommandsRpc.SetPeerPosition.request.safeParse(properties);
|
|
251
|
-
if (setPeerPosition.success) {
|
|
252
|
-
const value = setPeerPosition.data.position;
|
|
253
|
-
this._Position = value.length === 2 ? [value[0], value[1], 0] : value;
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
console.warn('unable to process SetPeerPosition command');
|
|
257
|
-
}
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
case 'UpdatePeer': {
|
|
261
|
-
const updatePeer = RoomCommandsRpc.UpdatePeer.request.safeParse(properties);
|
|
262
|
-
if (updatePeer.success) {
|
|
263
|
-
this._UserData = updatePeer.data.user_data;
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
console.warn('unable to process UpdatePeer command');
|
|
267
|
-
}
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
_RenewToken() {
|
|
273
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
274
|
-
this._LastTokenRenew = performance.now();
|
|
275
|
-
if (this.token.timeLeft > MinTokenLifetime)
|
|
276
|
-
return;
|
|
277
|
-
if (this._MainConnection === undefined || this._OwnPeerId === undefined) {
|
|
278
|
-
return failure('connection is not established');
|
|
279
|
-
}
|
|
280
|
-
const response = yield this._MainConnection.getReconnectToken(this._OwnPeerId);
|
|
281
|
-
if (response.type === 'Success') {
|
|
282
|
-
if (response.value.timeLeft > this._Token.timeLeft) {
|
|
283
|
-
this._Token = response.value;
|
|
284
|
-
}
|
|
285
|
-
return success();
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
return failure(`${this._MainConnection.address} rejected reconnect token request; ${response.reason}`);
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function contactGateway(gateway, token, signal) {
|
|
294
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
295
|
-
var _a, _b;
|
|
296
|
-
try {
|
|
297
|
-
const response = yield fetch(gateway, {
|
|
298
|
-
body: '{"jsonrpc":"2.0","id":1,"method":"Connect","params":{}}',
|
|
299
|
-
method: 'POST',
|
|
300
|
-
headers: {
|
|
301
|
-
'content-type': 'application/json',
|
|
302
|
-
authorization: `Bearer ${token}`,
|
|
303
|
-
},
|
|
304
|
-
signal,
|
|
305
|
-
});
|
|
306
|
-
// TODO: re-add validation once schemas are available in common
|
|
307
|
-
const body = yield response.json();
|
|
308
|
-
if ('result' in body) {
|
|
309
|
-
return Token.parse(body.result.token);
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
console.error(`gateway rejected token; ${(_b = (_a = body.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : 'malformed response'}`);
|
|
313
|
-
return success('rejected');
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
return failure(String(error));
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Result, Accept, MessagePackRpc, Notifications, Infer, Commands } from '@4players/odin-common';
|
|
2
|
-
export interface RpcMessage {
|
|
3
|
-
name: string;
|
|
4
|
-
properties: unknown;
|
|
5
|
-
}
|
|
6
|
-
export interface RpcRequest {
|
|
7
|
-
accept: Accept<Result<unknown> | 'closed' | 'timeout'>;
|
|
8
|
-
timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
9
|
-
}
|
|
10
|
-
export declare class RpcConnection {
|
|
11
|
-
private readonly stream;
|
|
12
|
-
private readonly _SendQueue;
|
|
13
|
-
private readonly _RecvQueue;
|
|
14
|
-
private readonly _EventQueue;
|
|
15
|
-
private readonly _Requests;
|
|
16
|
-
private readonly _Close;
|
|
17
|
-
private readonly _Writer;
|
|
18
|
-
private _NextId;
|
|
19
|
-
get closed(): boolean;
|
|
20
|
-
get closeSignal(): AbortSignal;
|
|
21
|
-
constructor(stream: WebSocket | WebTransportBidirectionalStream);
|
|
22
|
-
request(name: string, properties: unknown): Promise<Result<unknown> | 'closed'>;
|
|
23
|
-
request(name: string, properties: unknown, timeout: number): Promise<Result<unknown> | 'closed' | 'timeout'>;
|
|
24
|
-
notify(name: string, properties: unknown): void;
|
|
25
|
-
recv(): Promise<RpcMessage | 'closed'>;
|
|
26
|
-
close(): void;
|
|
27
|
-
private _TimeoutRequest;
|
|
28
|
-
private _Send;
|
|
29
|
-
private _OnWebSocketOpen;
|
|
30
|
-
private _OnRpcMessage;
|
|
31
|
-
private _OnResponse;
|
|
32
|
-
private _OnEvent;
|
|
33
|
-
private _Cleanup;
|
|
34
|
-
}
|
|
35
|
-
export interface TypedRpcMessage<Name extends string, Properties> extends RpcMessage {
|
|
36
|
-
name: Name;
|
|
37
|
-
properties: Properties;
|
|
38
|
-
}
|
|
39
|
-
export type RealizedEvents<E extends Notifications> = {
|
|
40
|
-
[Name in Extract<keyof E, string>]: TypedRpcMessage<Name, Infer<E[Name]>>;
|
|
41
|
-
}[Extract<keyof E, string>];
|
|
42
|
-
export declare class TypedRpcConnection<C extends Commands, E extends Notifications> {
|
|
43
|
-
readonly rpcConnection: RpcConnection;
|
|
44
|
-
private readonly _Commands;
|
|
45
|
-
private readonly _Events;
|
|
46
|
-
get closed(): boolean;
|
|
47
|
-
get closeSignal(): AbortSignal;
|
|
48
|
-
constructor(rpcConnection: RpcConnection, _Commands: C, _Events: E);
|
|
49
|
-
request<Name extends Extract<keyof C, string>>(name: Name, properties: Infer<C[Name]['request']>): Promise<Result<Infer<C[Name]['response']>> | 'closed'>;
|
|
50
|
-
request<Name extends Extract<keyof C, string>>(name: Name, properties: Infer<C[Name]['request']>, timeout: number): Promise<Result<Infer<C[Name]['response']>> | 'closed' | 'timeout'>;
|
|
51
|
-
notify<Name extends Extract<keyof C, string>>(name: Name, properties: Infer<C[Name]['request']>): void;
|
|
52
|
-
recv(): Promise<RealizedEvents<E> | 'closed'>;
|
|
53
|
-
close(): void;
|
|
54
|
-
}
|
|
55
|
-
export declare function parseRpc(buffer: ArrayBuffer): Result<MessagePackRpc>;
|
|
56
|
-
export declare function parseRpcMessage<E extends Notifications>(events: E, rpc: RpcMessage): RealizedEvents<E> | undefined;
|
|
57
|
-
export declare function isKnownRpcMessage<E extends Notifications>(events: E, name: string): name is Extract<keyof E, string>;
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-dupe-class-members */
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
import { failure, success, msgpackEncode, msgpackDecode, MessagePackRpcSchema, } from '@4players/odin-common';
|
|
12
|
-
import { getByteSize } from '../common/msgpack';
|
|
13
|
-
export class RpcConnection {
|
|
14
|
-
get closed() {
|
|
15
|
-
return this._Close.signal.aborted;
|
|
16
|
-
}
|
|
17
|
-
get closeSignal() {
|
|
18
|
-
return this._Close.signal;
|
|
19
|
-
}
|
|
20
|
-
constructor(stream) {
|
|
21
|
-
this.stream = stream;
|
|
22
|
-
this._SendQueue = [];
|
|
23
|
-
this._RecvQueue = [];
|
|
24
|
-
this._EventQueue = [];
|
|
25
|
-
this._Requests = new Map();
|
|
26
|
-
this._Close = new AbortController();
|
|
27
|
-
this._NextId = 1;
|
|
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
|
-
}
|
|
40
|
-
this._Close.signal.addEventListener('abort', this._Cleanup.bind(this), {
|
|
41
|
-
once: true,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
request(name, properties, timeout) {
|
|
45
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
-
if (this.closed) {
|
|
47
|
-
return Promise.resolve('closed');
|
|
48
|
-
}
|
|
49
|
-
const id = this._NextId;
|
|
50
|
-
const timeoutId = timeout === undefined
|
|
51
|
-
? undefined
|
|
52
|
-
: setTimeout(() => this._TimeoutRequest(id), timeout);
|
|
53
|
-
this._Send([0, id, name, properties]);
|
|
54
|
-
return new Promise((accept) => {
|
|
55
|
-
this._Requests.set(id, { accept, timeoutId });
|
|
56
|
-
this._NextId += 1;
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
notify(name, properties) {
|
|
61
|
-
if (!this.closed) {
|
|
62
|
-
this._Send([2, name, properties]);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
recv() {
|
|
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);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
return new Promise((accept) => this._RecvQueue.push(accept));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
close() {
|
|
78
|
-
this._Close.abort();
|
|
79
|
-
}
|
|
80
|
-
_TimeoutRequest(requestId) {
|
|
81
|
-
const request = this._Requests.get(requestId);
|
|
82
|
-
if (request === undefined)
|
|
83
|
-
return;
|
|
84
|
-
this._Requests.delete(requestId);
|
|
85
|
-
clearTimeout(request.timeoutId);
|
|
86
|
-
request.accept('timeout');
|
|
87
|
-
}
|
|
88
|
-
_Send(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
|
-
}
|
|
98
|
-
}
|
|
99
|
-
_OnWebSocketOpen(ev) {
|
|
100
|
-
for (const data of this._SendQueue) {
|
|
101
|
-
this._Send(data);
|
|
102
|
-
}
|
|
103
|
-
this._SendQueue.length = 0;
|
|
104
|
-
}
|
|
105
|
-
_OnRpcMessage(bytes) {
|
|
106
|
-
const rpc = parseRpc(bytes);
|
|
107
|
-
if (rpc.type !== 'Success') {
|
|
108
|
-
this.close();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
switch (rpc.value[0]) {
|
|
112
|
-
case 0:
|
|
113
|
-
this._Send([1, rpc.value[1], 'not supported', undefined]);
|
|
114
|
-
break;
|
|
115
|
-
case 1:
|
|
116
|
-
this._OnResponse(rpc.value[1], rpc.value[2], rpc.value[3]);
|
|
117
|
-
break;
|
|
118
|
-
case 2:
|
|
119
|
-
this._OnEvent(rpc.value[1], rpc.value[2]);
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
_OnResponse(id, error, result) {
|
|
124
|
-
const request = this._Requests.get(id);
|
|
125
|
-
if (request === undefined)
|
|
126
|
-
return;
|
|
127
|
-
clearTimeout(request.timeoutId);
|
|
128
|
-
request.accept(error === null ? success(result) : failure(error));
|
|
129
|
-
}
|
|
130
|
-
_OnEvent(name, properties) {
|
|
131
|
-
const receiver = this._RecvQueue.shift();
|
|
132
|
-
if (receiver !== undefined) {
|
|
133
|
-
receiver({ name, properties });
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
this._EventQueue.push({ name, properties });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
_Cleanup() {
|
|
140
|
-
var _a;
|
|
141
|
-
for (const request of this._Requests.values()) {
|
|
142
|
-
clearTimeout(request.timeoutId);
|
|
143
|
-
request.accept('closed');
|
|
144
|
-
}
|
|
145
|
-
this._Requests.clear();
|
|
146
|
-
for (const accept of this._RecvQueue) {
|
|
147
|
-
accept('closed');
|
|
148
|
-
}
|
|
149
|
-
this._RecvQueue.length = 0;
|
|
150
|
-
this._SendQueue.length = 0;
|
|
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
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
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;
|
|
170
|
-
this._Commands = _Commands;
|
|
171
|
-
this._Events = _Events;
|
|
172
|
-
}
|
|
173
|
-
request(name, properties, timeout) {
|
|
174
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
175
|
-
properties = this._Commands[name].request.parse(properties);
|
|
176
|
-
const response = yield (timeout !== undefined
|
|
177
|
-
? this.rpcConnection.request(name, properties, timeout)
|
|
178
|
-
: this.rpcConnection.request(name, properties));
|
|
179
|
-
if (response === 'closed' ||
|
|
180
|
-
response === 'timeout' ||
|
|
181
|
-
response.type === 'Failure') {
|
|
182
|
-
return response;
|
|
183
|
-
}
|
|
184
|
-
const parsed = this._Commands[name].response.safeParse(response.value);
|
|
185
|
-
if (parsed.success) {
|
|
186
|
-
response.value = parsed.data;
|
|
187
|
-
return response;
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
return failure(`response schema failed validation: ${parsed.error}`);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
notify(name, properties) {
|
|
195
|
-
properties = this._Commands[name].request.parse(properties);
|
|
196
|
-
this.rpcConnection.notify(name, properties);
|
|
197
|
-
}
|
|
198
|
-
recv() {
|
|
199
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
200
|
-
const event = yield this.rpcConnection.recv();
|
|
201
|
-
if (event === 'closed')
|
|
202
|
-
return event;
|
|
203
|
-
const parsed = parseRpcMessage(this._Events, event);
|
|
204
|
-
if (parsed !== undefined) {
|
|
205
|
-
return parsed;
|
|
206
|
-
}
|
|
207
|
-
if (!isKnownRpcMessage(this._Events, event.name)) {
|
|
208
|
-
console.warn(`dropping unknown event ${event.name}`);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
console.error(`schema validation for ${event.name} failed; closing connection`);
|
|
212
|
-
this.rpcConnection.close();
|
|
213
|
-
}
|
|
214
|
-
return 'closed';
|
|
215
|
-
});
|
|
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;
|
|
281
|
-
}
|
package/lib/plugin/token.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
}
|