@antha/multiplayer-core 0.0.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.
@@ -0,0 +1,104 @@
1
+ import { type JsonCompatibleValue } from '@augment-vir/common';
2
+ import { ListenTarget } from 'typed-event-target';
3
+ import { type ClientId } from '../multiplayer-id.js';
4
+ import { type WebrtcAnswer, type WebrtcOffer } from './web-rtc-communication.js';
5
+ /**
6
+ * Converts an `RTCSessionDescription` into a plain object so that shape validation works correctly.
7
+ * Browser `RTCSessionDescription` objects expose `type` and `sdp` as prototype getters rather than
8
+ * own data properties, which causes `Object.getOwnPropertyNames`-based checks (used by TypeBox /
9
+ * object-shape-tester) to miss them.
10
+ *
11
+ * @category Internal
12
+ */
13
+ export declare function toPlainSessionDescription(description: Readonly<RTCSessionDescription>): {
14
+ type: RTCSdpType;
15
+ sdp: string;
16
+ };
17
+ declare const WebrtcMessageEvent_base: (new (eventInitDict: {
18
+ bubbles?: boolean;
19
+ cancelable?: boolean;
20
+ composed?: boolean;
21
+ detail: any;
22
+ }) => import("typed-event-target").TypedCustomEvent<any, "webrtc-message">) & Pick<{
23
+ new (type: string, eventInitDict?: EventInit): Event;
24
+ prototype: Event;
25
+ readonly NONE: 0;
26
+ readonly CAPTURING_PHASE: 1;
27
+ readonly AT_TARGET: 2;
28
+ readonly BUBBLING_PHASE: 3;
29
+ }, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<any, "webrtc-message">, "type">;
30
+ /**
31
+ * An event that is omitted from {@link WebrtcController} when a WebRTC message is received.
32
+ *
33
+ * @category Internal
34
+ */
35
+ export declare class WebrtcMessageEvent<MessageData extends JsonCompatibleValue> extends WebrtcMessageEvent_base {
36
+ detail: MessageData;
37
+ }
38
+ declare const WebrtcConnectEvent_base: (new (eventInitDict: {
39
+ bubbles?: boolean;
40
+ cancelable?: boolean;
41
+ composed?: boolean;
42
+ detail: boolean;
43
+ }) => import("typed-event-target").TypedCustomEvent<boolean, "webrtc-connect">) & Pick<{
44
+ new (type: string, eventInitDict?: EventInit): Event;
45
+ prototype: Event;
46
+ readonly NONE: 0;
47
+ readonly CAPTURING_PHASE: 1;
48
+ readonly AT_TARGET: 2;
49
+ readonly BUBBLING_PHASE: 3;
50
+ }, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<boolean, "webrtc-connect">, "type">;
51
+ /**
52
+ * An event that is omitted from {@link WebrtcController} when the WebRTC connection is made or lost.
53
+ * `event.detail` is `true` if the connection has been made; `false` if the connection was lost.
54
+ *
55
+ * @category Internal
56
+ */
57
+ export declare class WebrtcConnectEvent extends WebrtcConnectEvent_base {
58
+ }
59
+ /**
60
+ * All events fro {@link WebrtcController}.
61
+ *
62
+ * @category Internal
63
+ */
64
+ export type WebrtcEvents<MessageData extends JsonCompatibleValue> = WebrtcMessageEvent<MessageData> | WebrtcConnectEvent;
65
+ /**
66
+ * A single peer-to-peer WebRTC controller. It is to be used like this:
67
+ *
68
+ * 1. Call {@link WebrtcController.createOffer} to start off a WebRTC handshake. Send this offer to
69
+ * another WebRTC connection or {@link WebrtcController} instance.
70
+ * 2. Call {@link WebrtcController.createAnswer} to accept someone else's WebRTC handshake offer and
71
+ * create a WebRTC handshake answer. Send this answer back to whoever sent the WebRTC offer.
72
+ * 3. Call {@link WebrtcController.acceptAnswer} on the {@link WebrtcController} instance that first
73
+ * created the offer.
74
+ *
75
+ * If everything went well, after those 3 steps you'll now have a live WebRTC connection!
76
+ *
77
+ * @category Internal
78
+ */
79
+ export declare class WebrtcController<MessageData extends JsonCompatibleValue> extends ListenTarget<WebrtcEvents<MessageData>> {
80
+ readonly clientId: ClientId;
81
+ private dataChannel;
82
+ private connection;
83
+ /** Indicates whether the WebRTC connection is live or not. */
84
+ readonly isConnected: boolean;
85
+ constructor(clientId: ClientId);
86
+ /** Create a WebRTC offer. This is the first step in the WebRTC handshake process. */
87
+ createOffer(stunServerUrls: ReadonlyArray<string>): Promise<WebrtcOffer>;
88
+ /** Accepts a WebRTC answer. This is the third (and last) step in the WebRTC handshake process. */
89
+ acceptAnswer(rawAnswer: string | Readonly<RTCSessionDescriptionInit>): Promise<void>;
90
+ /**
91
+ * Accepts a WebRTC offer and creates a WebRTC answer. This is the second step in the WebRTC
92
+ * handshake process.
93
+ */
94
+ createAnswer(rawOffer: string | Readonly<RTCSessionDescriptionInit>, stunServerUrls: ReadonlyArray<string>): Promise<WebrtcAnswer>;
95
+ /**
96
+ * Send a message to the other peer in the WebRTC connection. This will throw an error if the
97
+ * connection has not been established yet.
98
+ */
99
+ sendMessage(data: Readonly<MessageData>): void;
100
+ destroy(): void;
101
+ private handleDataChannel;
102
+ private createConnection;
103
+ }
104
+ export {};
@@ -0,0 +1,170 @@
1
+ import { assert, assertWrap, check } from '@augment-vir/assert';
2
+ import { addPrefix, DeferredPromise, makeWritable, wrapInTry, } from '@augment-vir/common';
3
+ import { assertValidShape } from 'object-shape-tester';
4
+ import { defineTypedCustomEvent, ListenTarget } from 'typed-event-target';
5
+ import { webrtcAnswerShape, webrtcOfferShape, } from './web-rtc-communication.js';
6
+ /**
7
+ * Converts an `RTCSessionDescription` into a plain object so that shape validation works correctly.
8
+ * Browser `RTCSessionDescription` objects expose `type` and `sdp` as prototype getters rather than
9
+ * own data properties, which causes `Object.getOwnPropertyNames`-based checks (used by TypeBox /
10
+ * object-shape-tester) to miss them.
11
+ *
12
+ * @category Internal
13
+ */
14
+ export function toPlainSessionDescription(description) {
15
+ return {
16
+ type: description.type,
17
+ sdp: description.sdp,
18
+ };
19
+ }
20
+ /**
21
+ * An event that is omitted from {@link WebrtcController} when a WebRTC message is received.
22
+ *
23
+ * @category Internal
24
+ */
25
+ export class WebrtcMessageEvent extends defineTypedCustomEvent()('webrtc-message') {
26
+ }
27
+ /**
28
+ * An event that is omitted from {@link WebrtcController} when the WebRTC connection is made or lost.
29
+ * `event.detail` is `true` if the connection has been made; `false` if the connection was lost.
30
+ *
31
+ * @category Internal
32
+ */
33
+ export class WebrtcConnectEvent extends defineTypedCustomEvent()('webrtc-connect') {
34
+ }
35
+ function formatStunServerUrls(stunServerUrls) {
36
+ return stunServerUrls.map((stunServerUrl) => {
37
+ return {
38
+ urls: addPrefix({
39
+ value: stunServerUrl,
40
+ prefix: 'stun:',
41
+ }),
42
+ };
43
+ });
44
+ }
45
+ /**
46
+ * A single peer-to-peer WebRTC controller. It is to be used like this:
47
+ *
48
+ * 1. Call {@link WebrtcController.createOffer} to start off a WebRTC handshake. Send this offer to
49
+ * another WebRTC connection or {@link WebrtcController} instance.
50
+ * 2. Call {@link WebrtcController.createAnswer} to accept someone else's WebRTC handshake offer and
51
+ * create a WebRTC handshake answer. Send this answer back to whoever sent the WebRTC offer.
52
+ * 3. Call {@link WebrtcController.acceptAnswer} on the {@link WebrtcController} instance that first
53
+ * created the offer.
54
+ *
55
+ * If everything went well, after those 3 steps you'll now have a live WebRTC connection!
56
+ *
57
+ * @category Internal
58
+ */
59
+ export class WebrtcController extends ListenTarget {
60
+ clientId;
61
+ dataChannel;
62
+ connection;
63
+ /** Indicates whether the WebRTC connection is live or not. */
64
+ isConnected = false;
65
+ constructor(clientId) {
66
+ super();
67
+ this.clientId = clientId;
68
+ }
69
+ /** Create a WebRTC offer. This is the first step in the WebRTC handshake process. */
70
+ async createOffer(stunServerUrls) {
71
+ const candidatePromise = this.createConnection(stunServerUrls);
72
+ assert.isDefined(this.connection);
73
+ this.handleDataChannel(this.connection.createDataChannel('chat'));
74
+ await this.connection.setLocalDescription(await this.connection.createOffer());
75
+ await candidatePromise;
76
+ const offer = toPlainSessionDescription(assertWrap.isDefined(this.connection.localDescription));
77
+ assertValidShape(offer, webrtcOfferShape);
78
+ return offer;
79
+ }
80
+ /** Accepts a WebRTC answer. This is the third (and last) step in the WebRTC handshake process. */
81
+ async acceptAnswer(rawAnswer) {
82
+ const answer = check.isString(rawAnswer) ? JSON.parse(rawAnswer) : rawAnswer;
83
+ assert.isDefined(this.connection);
84
+ assertValidShape(answer, webrtcAnswerShape);
85
+ await this.connection.setRemoteDescription(answer);
86
+ }
87
+ /**
88
+ * Accepts a WebRTC offer and creates a WebRTC answer. This is the second step in the WebRTC
89
+ * handshake process.
90
+ */
91
+ async createAnswer(rawOffer, stunServerUrls) {
92
+ const offer = check.isString(rawOffer)
93
+ ? JSON.parse(rawOffer)
94
+ : rawOffer;
95
+ const candidatePromise = this.createConnection(stunServerUrls);
96
+ assert.isDefined(this.connection);
97
+ this.connection.addEventListener('datachannel', (event) => {
98
+ this.handleDataChannel(event.channel);
99
+ });
100
+ await this.connection.setRemoteDescription(offer);
101
+ await this.connection.setLocalDescription(await this.connection.createAnswer());
102
+ if (stunServerUrls.length) {
103
+ await candidatePromise;
104
+ }
105
+ const answer = toPlainSessionDescription(assertWrap.isDefined(this.connection.localDescription));
106
+ assertValidShape(answer, webrtcAnswerShape);
107
+ return answer;
108
+ }
109
+ /**
110
+ * Send a message to the other peer in the WebRTC connection. This will throw an error if the
111
+ * connection has not been established yet.
112
+ */
113
+ sendMessage(data) {
114
+ assert.isTrue(this.isConnected, `There is no WebRTC connection to send a message to from ${this.clientId}.`);
115
+ assert.isDefined(this.dataChannel, `There is no WebRTC connection to send a message to from ${this.clientId}.`);
116
+ this.dataChannel.send(JSON.stringify(data));
117
+ }
118
+ destroy() {
119
+ this.dataChannel?.close();
120
+ this.connection?.close();
121
+ super.destroy();
122
+ }
123
+ handleDataChannel(dataChannel) {
124
+ this.dataChannel?.close();
125
+ this.dataChannel = dataChannel;
126
+ this.dataChannel.addEventListener('open', () => {
127
+ makeWritable(this).isConnected = true;
128
+ this.dispatch(new WebrtcConnectEvent({
129
+ detail: true,
130
+ }));
131
+ });
132
+ this.dataChannel.addEventListener('closing', () => {
133
+ makeWritable(this).isConnected = false;
134
+ this.dispatch(new WebrtcConnectEvent({
135
+ detail: false,
136
+ }));
137
+ });
138
+ this.dataChannel.addEventListener('message', (event) => {
139
+ const detail = wrapInTry(() => (check.isString(event.data) ? JSON.parse(event.data) : event.data), {
140
+ fallbackValue: event.data,
141
+ });
142
+ this.dispatch(new WebrtcMessageEvent({
143
+ detail,
144
+ }));
145
+ });
146
+ }
147
+ createConnection(stunServerUrls) {
148
+ if (this.connection) {
149
+ throw new Error('Connection already created!');
150
+ }
151
+ const deferredIceCandidatePromise = new DeferredPromise();
152
+ const iceCandidateListener = (event) => {
153
+ // all candidates are done
154
+ if (!event.candidate) {
155
+ assert.isDefined(this.connection);
156
+ deferredIceCandidatePromise.resolve();
157
+ this.connection.removeEventListener('icecandidate', iceCandidateListener);
158
+ }
159
+ };
160
+ this.connection = new RTCPeerConnection({
161
+ iceServers: formatStunServerUrls(stunServerUrls),
162
+ });
163
+ this.connection.addEventListener('icecandidate', iceCandidateListener);
164
+ /**
165
+ * This must be awaited so the candidate list can finish populating before we present the
166
+ * offer to the user.
167
+ */
168
+ return deferredIceCandidatePromise.promise;
169
+ }
170
+ }
@@ -0,0 +1,263 @@
1
+ import { type JsonCompatibleValue, type MaybePromise, type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type RequireExactlyOne } from 'type-fest';
3
+ import { ListenTarget } from 'typed-event-target';
4
+ import { type MultiplayerConnectClientMessage } from '../multiplayer-api/multiplayer-api.js';
5
+ import { type MultiplayerApiClient } from '../multiplayer-api/multiplayer-client.js';
6
+ import { type ClientId } from '../multiplayer-id.js';
7
+ import { MultiplayerWebSocketMessageType } from './web-rtc-communication.js';
8
+ declare const WebrtcMultiplayerMessageEvent_base: (new (eventInitDict: {
9
+ bubbles?: boolean;
10
+ cancelable?: boolean;
11
+ composed?: boolean;
12
+ detail: any;
13
+ }) => import("typed-event-target").TypedCustomEvent<any, "webrtc-multiplayer-message">) & Pick<{
14
+ new (type: string, eventInitDict?: EventInit): Event;
15
+ prototype: Event;
16
+ readonly NONE: 0;
17
+ readonly CAPTURING_PHASE: 1;
18
+ readonly AT_TARGET: 2;
19
+ readonly BUBBLING_PHASE: 3;
20
+ }, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<any, "webrtc-multiplayer-message">, "type">;
21
+ /**
22
+ * An event that is omitted from {@link WebrtcController} when a WebRTC message is received.
23
+ *
24
+ * @category Internal
25
+ */
26
+ export declare class WebrtcMultiplayerMessageEvent<MessageData extends JsonCompatibleValue> extends WebrtcMultiplayerMessageEvent_base {
27
+ readonly sourceClientId: ClientId;
28
+ detail: MessageData;
29
+ constructor(sourceClientId: ClientId, detail: MessageData);
30
+ }
31
+ /**
32
+ * Type for data in {@link WebrtcMultiplayerConnectionUpdateEvent}.
33
+ *
34
+ * @category Internal
35
+ */
36
+ export type MultiplayerConnectionUpdate = RequireExactlyOne<{
37
+ newHost: ClientId;
38
+ newMember: ClientId;
39
+ lostHost: ClientId;
40
+ lostMember: ClientId;
41
+ }>;
42
+ declare const WebrtcMultiplayerConnectionUpdateEvent_base: (new (eventInitDict: {
43
+ bubbles?: boolean;
44
+ cancelable?: boolean;
45
+ composed?: boolean;
46
+ detail: ((Required<Pick<{
47
+ newHost: ClientId;
48
+ newMember: ClientId;
49
+ lostHost: ClientId;
50
+ lostMember: ClientId;
51
+ }, "newHost">> & Partial<Record<"newMember" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
52
+ newHost: ClientId;
53
+ newMember: ClientId;
54
+ lostHost: ClientId;
55
+ lostMember: ClientId;
56
+ }, "newMember">> & Partial<Record<"newHost" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
57
+ newHost: ClientId;
58
+ newMember: ClientId;
59
+ lostHost: ClientId;
60
+ lostMember: ClientId;
61
+ }, "lostHost">> & Partial<Record<"newHost" | "newMember" | "lostMember", never>>) | (Required<Pick<{
62
+ newHost: ClientId;
63
+ newMember: ClientId;
64
+ lostHost: ClientId;
65
+ lostMember: ClientId;
66
+ }, "lostMember">> & Partial<Record<"newHost" | "newMember" | "lostHost", never>>)) & Omit<{
67
+ newHost: ClientId;
68
+ newMember: ClientId;
69
+ lostHost: ClientId;
70
+ lostMember: ClientId;
71
+ }, "newHost" | "newMember" | "lostHost" | "lostMember">;
72
+ }) => import("typed-event-target").TypedCustomEvent<((Required<Pick<{
73
+ newHost: ClientId;
74
+ newMember: ClientId;
75
+ lostHost: ClientId;
76
+ lostMember: ClientId;
77
+ }, "newHost">> & Partial<Record<"newMember" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
78
+ newHost: ClientId;
79
+ newMember: ClientId;
80
+ lostHost: ClientId;
81
+ lostMember: ClientId;
82
+ }, "newMember">> & Partial<Record<"newHost" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
83
+ newHost: ClientId;
84
+ newMember: ClientId;
85
+ lostHost: ClientId;
86
+ lostMember: ClientId;
87
+ }, "lostHost">> & Partial<Record<"newHost" | "newMember" | "lostMember", never>>) | (Required<Pick<{
88
+ newHost: ClientId;
89
+ newMember: ClientId;
90
+ lostHost: ClientId;
91
+ lostMember: ClientId;
92
+ }, "lostMember">> & Partial<Record<"newHost" | "newMember" | "lostHost", never>>)) & Omit<{
93
+ newHost: ClientId;
94
+ newMember: ClientId;
95
+ lostHost: ClientId;
96
+ lostMember: ClientId;
97
+ }, "newHost" | "newMember" | "lostHost" | "lostMember">, "webrtc-multiplayer-connection-update">) & Pick<{
98
+ new (type: string, eventInitDict?: EventInit): Event;
99
+ prototype: Event;
100
+ readonly NONE: 0;
101
+ readonly CAPTURING_PHASE: 1;
102
+ readonly AT_TARGET: 2;
103
+ readonly BUBBLING_PHASE: 3;
104
+ }, "prototype" | "NONE" | "CAPTURING_PHASE" | "AT_TARGET" | "BUBBLING_PHASE"> & Pick<import("typed-event-target").TypedCustomEvent<((Required<Pick<{
105
+ newHost: ClientId;
106
+ newMember: ClientId;
107
+ lostHost: ClientId;
108
+ lostMember: ClientId;
109
+ }, "newHost">> & Partial<Record<"newMember" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
110
+ newHost: ClientId;
111
+ newMember: ClientId;
112
+ lostHost: ClientId;
113
+ lostMember: ClientId;
114
+ }, "newMember">> & Partial<Record<"newHost" | "lostHost" | "lostMember", never>>) | (Required<Pick<{
115
+ newHost: ClientId;
116
+ newMember: ClientId;
117
+ lostHost: ClientId;
118
+ lostMember: ClientId;
119
+ }, "lostHost">> & Partial<Record<"newHost" | "newMember" | "lostMember", never>>) | (Required<Pick<{
120
+ newHost: ClientId;
121
+ newMember: ClientId;
122
+ lostHost: ClientId;
123
+ lostMember: ClientId;
124
+ }, "lostMember">> & Partial<Record<"newHost" | "newMember" | "lostHost", never>>)) & Omit<{
125
+ newHost: ClientId;
126
+ newMember: ClientId;
127
+ lostHost: ClientId;
128
+ lostMember: ClientId;
129
+ }, "newHost" | "newMember" | "lostHost" | "lostMember">, "webrtc-multiplayer-connection-update">, "type">;
130
+ /**
131
+ * An event that is omitted from {@link WebrtcMultiplayerController} when the multiplayer room host
132
+ * is updated.
133
+ *
134
+ * @category Internal
135
+ */
136
+ export declare class WebrtcMultiplayerConnectionUpdateEvent extends WebrtcMultiplayerConnectionUpdateEvent_base {
137
+ }
138
+ /**
139
+ * A helper for creating a new empty room.
140
+ *
141
+ * @category Internal
142
+ */
143
+ export declare function createNewRoom(params?: Readonly<PartialWithUndefined<Omit<RoomInput, 'roomId'>>>): RoomInput;
144
+ /**
145
+ * Room selection input for {@link WebrtcMultiplayerController}.
146
+ *
147
+ * @category Internal
148
+ */
149
+ export type RoomInput = Pick<Extract<MultiplayerConnectClientMessage, {
150
+ type: MultiplayerWebSocketMessageType.Offer;
151
+ }>, 'roomPassword' | 'roomId' | 'roomName'>;
152
+ /**
153
+ * This is used to check if a new WebRTC connection should be allowed. This will only be triggered
154
+ * on a room host client. Return `true`
155
+ *
156
+ * @category Internal
157
+ */
158
+ export type ShouldAllowConnectionCheck<Controller> = (data: {
159
+ connectingClientId: ClientId;
160
+ controller: Controller;
161
+ }) => MaybePromise<boolean>;
162
+ /**
163
+ * A controller that connects to the multiplayer api and establishes a WebRTC connection to the
164
+ * selected room, or, if the room does not exist yet, creates the room and becomes the host.
165
+ *
166
+ * Make sure, after constructing this class, to call
167
+ * {@link WebrtcMultiplayerController.initConnection} when you're ready to being the connection.
168
+ *
169
+ * @category Internal
170
+ */
171
+ export declare class WebrtcMultiplayerController<MessageData extends JsonCompatibleValue = any> extends ListenTarget<WebrtcMultiplayerMessageEvent<MessageData> | WebrtcMultiplayerConnectionUpdateEvent> {
172
+ private readonly gameId;
173
+ private readonly multiplayerApiClient;
174
+ readonly stunServerUrls: ReadonlyArray<string>;
175
+ readonly multiplayerRoom: Readonly<RoomInput>;
176
+ /** The randomized client id for this controller and client. */
177
+ readonly clientId: ClientId;
178
+ /**
179
+ * This is fired when a WebRTC peer attempts to connect to the host client (this will only
180
+ * be fired if your client is the host). Return `true` to accept the connection. Return
181
+ * `false` to reject it.
182
+ *
183
+ * @default accept all connections
184
+ */
185
+ private readonly shouldAllowConnectionCheck;
186
+ readonly hostClientId: ClientId | undefined;
187
+ /**
188
+ * Connections between multiple WebRTC peers.
189
+ *
190
+ * A connection with the current client's id is the init connection.
191
+ */
192
+ private connections;
193
+ private webSocket;
194
+ private readonly clientSecret;
195
+ readonly isDestroyed: boolean;
196
+ constructor(gameId: string, multiplayerApiClient: Readonly<MultiplayerApiClient>, stunServerUrls: ReadonlyArray<string>, multiplayerRoom: Readonly<RoomInput>,
197
+ /** The randomized client id for this controller and client. */
198
+ clientId?: ClientId,
199
+ /**
200
+ * This is fired when a WebRTC peer attempts to connect to the host client (this will only
201
+ * be fired if your client is the host). Return `true` to accept the connection. Return
202
+ * `false` to reject it.
203
+ *
204
+ * @default accept all connections
205
+ */
206
+ shouldAllowConnectionCheck?: ShouldAllowConnectionCheck<WebrtcMultiplayerController<MessageData>>);
207
+ /**
208
+ * If this controller is the host, it'll behave differently:
209
+ *
210
+ * - Hosts hold WebRTC connections to all clients (non-hosts only hold a WebRTC connection to the
211
+ * host).
212
+ * - Hosts hold a WebSocket connection to the signal server so they can receive more clients at
213
+ * any time.
214
+ */
215
+ isHost(): boolean;
216
+ /**
217
+ * Get all connected client ids.
218
+ *
219
+ * - For host clients, this indicates how many member clients are connected to the host client,
220
+ * _not_ including the host itself.
221
+ * - For non-host clients, this only lists the local connection used to reach the host.
222
+ *
223
+ * For host clients, this does ont include the host client id whereas
224
+ * {@link WebrtcMultiplayerController.getAllClientIds} does.
225
+ */
226
+ getConnectedClientIds(): ClientId[];
227
+ /**
228
+ * Get all room client ids.
229
+ *
230
+ * - For host clients, this indicates how many clients are connected to the room, including the
231
+ * host client itself.
232
+ * - For non-host clients, this includes the member client and the host client once connected.
233
+ *
234
+ * For host clients, this includes the host client id whereas
235
+ * {@link WebrtcMultiplayerController.getConnectedClientIds} does not.
236
+ */
237
+ getAllClientIds(): ClientId[];
238
+ /** Indicates whether ths client is connected to a multiplayer room. */
239
+ isConnected(): boolean;
240
+ /** Destroy this controller and clean everything up. */
241
+ destroy(): void;
242
+ /**
243
+ * Send a message to the room participants.
244
+ *
245
+ * - If the current client is the room host, this message is sent to all other room clients.
246
+ * - If the current client is just a room member (not the host), the message is sent to the host.
247
+ */
248
+ sendMessage(data: Readonly<MessageData>): void;
249
+ /** Send a message to just a single client. This is only allowed on a host client. */
250
+ sendToOnlyOneClient(clientId: ClientId, data: Readonly<MessageData>): void;
251
+ /**
252
+ * Call this to connect to the multiplayer server.
253
+ *
254
+ * @returns Whether or not the connection was initialized (it won't be initialized, for example,
255
+ * if the WebRTC connections already exist).
256
+ */
257
+ initConnection(): Promise<boolean>;
258
+ private sendHostPing;
259
+ private connectionQueue;
260
+ private setupWebSocket;
261
+ private createNewConnection;
262
+ }
263
+ export {};