glitch-javascript-sdk 3.1.4 → 3.1.5

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,1012 @@
1
+ import MultiplayerRoute from "../routes/MultiplayerRoute";
2
+ import Requests from "../util/Requests";
3
+ import Response from "../util/Response";
4
+ import { AxiosPromise } from "axios";
5
+
6
+ export type MultiplayerLobbyType = 'public' | 'invisible' | 'friends_only' | 'private';
7
+ export type MultiplayerLobbyState = 'waiting' | 'ready' | 'in_game' | 'closed';
8
+ export type MultiplayerLobbyMemberStatus = 'joined' | 'left' | 'disconnected' | 'kicked' | 'banned';
9
+ export type MultiplayerLobbyMessageType = 'chat' | 'binary' | 'system' | 'ready' | 'invite' | 'kick' | 'voice';
10
+ export type MultiplayerServerType = 'dedicated' | 'listen' | 'relay';
11
+ export type MultiplayerServerStatus = 'active' | 'available' | 'draining' | 'offline';
12
+ export type MultiplayerTransport = 'udp' | 'tcp' | 'webrtc' | 'relay';
13
+ export type MultiplayerSessionState = 'reserved' | 'active' | 'released' | 'expired';
14
+ export type MultiplayerAuthTicketStatus = 'active' | 'consumed' | 'revoked' | 'expired';
15
+ export type MultiplayerFavoriteKind = 'favorite' | 'history';
16
+ export type MultiplayerVoiceProvider = 'glitch_relay' | 'external';
17
+ export type MultiplayerVoiceTopology = 'lobby' | 'server' | 'party' | 'proximity';
18
+ export type MultiplayerVoiceState = 'active' | 'closed';
19
+ export type MultiplayerVoiceCodec = 'opus' | 'pcm16' | 'aac';
20
+ export type MultiplayerVoiceParticipantStatus = 'joined' | 'left' | 'muted' | 'kicked';
21
+ export type MultiplayerVoicePacketType = 'audio' | 'speaking' | 'mute_state' | 'offer' | 'answer' | 'ice' | 'control';
22
+
23
+ export type MultiplayerMetadata = Record<string, any>;
24
+
25
+ export interface MultiplayerLobbyMember {
26
+ id: string;
27
+ lobby_id: string;
28
+ player_id: string;
29
+ user_id?: string | null;
30
+ display_name?: string | null;
31
+ status: MultiplayerLobbyMemberStatus;
32
+ ready: boolean;
33
+ member_data: MultiplayerMetadata;
34
+ joined_at?: string | null;
35
+ last_seen_at?: string | null;
36
+ left_at?: string | null;
37
+ }
38
+
39
+ export interface MultiplayerServer {
40
+ id: string;
41
+ title_id: string;
42
+ name: string;
43
+ server_type: MultiplayerServerType;
44
+ status: MultiplayerServerStatus;
45
+ region?: string | null;
46
+ build_version?: string | null;
47
+ host?: string | null;
48
+ game_port?: number | null;
49
+ query_port?: number | null;
50
+ transport: MultiplayerTransport;
51
+ connection_uri?: string | null;
52
+ max_players: number;
53
+ current_players: number;
54
+ bot_players: number;
55
+ secure: boolean;
56
+ password_protected: boolean;
57
+ private: boolean;
58
+ tags: string[];
59
+ rules: MultiplayerMetadata;
60
+ metadata: MultiplayerMetadata;
61
+ last_heartbeat_at?: string | null;
62
+ expires_at?: string | null;
63
+ created_at?: string | null;
64
+ updated_at?: string | null;
65
+ }
66
+
67
+ export interface MultiplayerLobby {
68
+ id: string;
69
+ title_id: string;
70
+ server_id?: string | null;
71
+ owner_player_id: string;
72
+ owner_user_id?: string | null;
73
+ lobby_type: MultiplayerLobbyType;
74
+ state: MultiplayerLobbyState;
75
+ joinable: boolean;
76
+ max_members: number;
77
+ member_count: number;
78
+ region?: string | null;
79
+ game_mode?: string | null;
80
+ map_name?: string | null;
81
+ skill_band?: number | null;
82
+ metadata: MultiplayerMetadata;
83
+ last_activity_at?: string | null;
84
+ expires_at?: string | null;
85
+ created_at?: string | null;
86
+ updated_at?: string | null;
87
+ members?: MultiplayerLobbyMember[];
88
+ server?: MultiplayerServer | null;
89
+ }
90
+
91
+ export interface MultiplayerLobbyMessage {
92
+ id: string;
93
+ lobby_id: string;
94
+ player_id: string;
95
+ user_id?: string | null;
96
+ message_type: MultiplayerLobbyMessageType;
97
+ payload: MultiplayerMetadata;
98
+ sequence: number;
99
+ created_at?: string | null;
100
+ updated_at?: string | null;
101
+ }
102
+
103
+ export interface MultiplayerSession {
104
+ id: string;
105
+ title_id: string;
106
+ server_id?: string | null;
107
+ lobby_id?: string | null;
108
+ player_id: string;
109
+ user_id?: string | null;
110
+ state: MultiplayerSessionState;
111
+ connection_payload: MultiplayerMetadata;
112
+ last_heartbeat_at?: string | null;
113
+ expires_at?: string | null;
114
+ started_at?: string | null;
115
+ ended_at?: string | null;
116
+ created_at?: string | null;
117
+ updated_at?: string | null;
118
+ }
119
+
120
+ export interface MultiplayerAuthTicket {
121
+ id: string;
122
+ title_id: string;
123
+ player_id: string;
124
+ user_id?: string | null;
125
+ remote_identity?: string | null;
126
+ status: MultiplayerAuthTicketStatus;
127
+ issued_at?: string | null;
128
+ expires_at?: string | null;
129
+ consumed_at?: string | null;
130
+ created_at?: string | null;
131
+ updated_at?: string | null;
132
+ }
133
+
134
+ export interface MultiplayerServerFavorite {
135
+ id: string;
136
+ title_id: string;
137
+ server_id?: string | null;
138
+ user_id?: string | null;
139
+ player_id: string;
140
+ kind: MultiplayerFavoriteKind;
141
+ name?: string | null;
142
+ host?: string | null;
143
+ game_port?: number | null;
144
+ query_port?: number | null;
145
+ metadata: MultiplayerMetadata;
146
+ last_played_at?: string | null;
147
+ created_at?: string | null;
148
+ updated_at?: string | null;
149
+ }
150
+
151
+ export interface MultiplayerVoiceParticipant {
152
+ id: string;
153
+ voice_room_id: string;
154
+ player_id: string;
155
+ user_id?: string | null;
156
+ display_name?: string | null;
157
+ status: MultiplayerVoiceParticipantStatus;
158
+ muted: boolean;
159
+ deafened: boolean;
160
+ speaking: boolean;
161
+ last_sequence: number;
162
+ metadata: MultiplayerMetadata;
163
+ joined_at?: string | null;
164
+ last_heartbeat_at?: string | null;
165
+ left_at?: string | null;
166
+ expires_at?: string | null;
167
+ created_at?: string | null;
168
+ updated_at?: string | null;
169
+ }
170
+
171
+ export interface MultiplayerVoiceRoom {
172
+ id: string;
173
+ title_id: string;
174
+ lobby_id?: string | null;
175
+ server_id?: string | null;
176
+ owner_player_id: string;
177
+ owner_user_id?: string | null;
178
+ provider: MultiplayerVoiceProvider;
179
+ topology: MultiplayerVoiceTopology;
180
+ state: MultiplayerVoiceState;
181
+ region?: string | null;
182
+ codec: MultiplayerVoiceCodec;
183
+ sample_rate: number;
184
+ bitrate: number;
185
+ frame_duration_ms: 10 | 20 | 40 | 60;
186
+ channels: 1 | 2;
187
+ max_participants: number;
188
+ participant_count: number;
189
+ recording_allowed: boolean;
190
+ moderation_enabled: boolean;
191
+ connection_config: MultiplayerMetadata;
192
+ metadata: MultiplayerMetadata;
193
+ last_activity_at?: string | null;
194
+ expires_at?: string | null;
195
+ created_at?: string | null;
196
+ updated_at?: string | null;
197
+ participants?: MultiplayerVoiceParticipant[];
198
+ }
199
+
200
+ export interface MultiplayerVoicePacket {
201
+ id: string;
202
+ voice_room_id: string;
203
+ participant_id?: string | null;
204
+ player_id: string;
205
+ packet_type: MultiplayerVoicePacketType;
206
+ payload: string;
207
+ sequence: number;
208
+ duration_ms?: number | null;
209
+ sent_at?: string | null;
210
+ created_at?: string | null;
211
+ updated_at?: string | null;
212
+ }
213
+
214
+ export interface MultiplayerLobbySearchParams {
215
+ region?: string;
216
+ game_mode?: string;
217
+ map_name?: string;
218
+ lobby_type?: MultiplayerLobbyType;
219
+ skill_band?: number;
220
+ limit?: number;
221
+ }
222
+
223
+ export interface MultiplayerCreateLobbyRequest {
224
+ player_id?: string;
225
+ display_name?: string;
226
+ member_data?: MultiplayerMetadata;
227
+ lobby_type?: MultiplayerLobbyType;
228
+ state?: MultiplayerLobbyState;
229
+ joinable?: boolean;
230
+ max_members?: number;
231
+ region?: string;
232
+ game_mode?: string;
233
+ map_name?: string;
234
+ skill_band?: number;
235
+ metadata?: MultiplayerMetadata;
236
+ expires_at?: string;
237
+ }
238
+
239
+ export interface MultiplayerJoinLobbyRequest {
240
+ player_id?: string;
241
+ display_name?: string;
242
+ ready?: boolean;
243
+ member_data?: MultiplayerMetadata;
244
+ }
245
+
246
+ export interface MultiplayerLeaveLobbyRequest {
247
+ player_id?: string;
248
+ }
249
+
250
+ export interface MultiplayerUpdateLobbyRequest {
251
+ player_id?: string;
252
+ lobby_type?: MultiplayerLobbyType;
253
+ state?: MultiplayerLobbyState;
254
+ joinable?: boolean;
255
+ max_members?: number;
256
+ region?: string;
257
+ game_mode?: string;
258
+ map_name?: string;
259
+ skill_band?: number;
260
+ metadata?: MultiplayerMetadata;
261
+ expires_at?: string;
262
+ }
263
+
264
+ export interface MultiplayerSetLobbyServerRequest {
265
+ player_id?: string;
266
+ server_id: string;
267
+ state?: MultiplayerLobbyState;
268
+ joinable?: boolean;
269
+ }
270
+
271
+ export interface MultiplayerLobbyMessagesParams {
272
+ after_sequence?: number;
273
+ limit?: number;
274
+ }
275
+
276
+ export interface MultiplayerSendLobbyMessageRequest {
277
+ player_id?: string;
278
+ message_type?: MultiplayerLobbyMessageType;
279
+ payload: MultiplayerMetadata;
280
+ }
281
+
282
+ export interface MultiplayerServerBrowserParams {
283
+ region?: string;
284
+ build_version?: string;
285
+ transport?: MultiplayerTransport;
286
+ status?: MultiplayerServerStatus;
287
+ secure?: boolean;
288
+ include_private?: boolean;
289
+ limit?: number;
290
+ }
291
+
292
+ export interface MultiplayerRegisterServerRequest {
293
+ name: string;
294
+ server_type?: MultiplayerServerType;
295
+ status?: MultiplayerServerStatus;
296
+ region?: string;
297
+ build_version?: string;
298
+ host?: string;
299
+ game_port?: number;
300
+ query_port?: number;
301
+ transport?: MultiplayerTransport;
302
+ connection_uri?: string;
303
+ max_players?: number;
304
+ current_players?: number;
305
+ bot_players?: number;
306
+ secure?: boolean;
307
+ password_protected?: boolean;
308
+ private?: boolean;
309
+ tags?: string[];
310
+ rules?: MultiplayerMetadata;
311
+ metadata?: MultiplayerMetadata;
312
+ expires_at?: string;
313
+ }
314
+
315
+ export interface MultiplayerRegisterServerResponse {
316
+ server: MultiplayerServer;
317
+ server_token: string;
318
+ }
319
+
320
+ export interface MultiplayerServerHeartbeatRequest {
321
+ server_token: string;
322
+ status?: MultiplayerServerStatus;
323
+ current_players?: number;
324
+ bot_players?: number;
325
+ rules?: MultiplayerMetadata;
326
+ metadata?: MultiplayerMetadata;
327
+ }
328
+
329
+ export interface MultiplayerReserveServerRequest {
330
+ player_id?: string;
331
+ lobby_id?: string;
332
+ ttl_minutes?: number;
333
+ }
334
+
335
+ export interface MultiplayerReserveServerResponse {
336
+ session: MultiplayerSession;
337
+ reservation_token: string;
338
+ }
339
+
340
+ export interface MultiplayerSessionHeartbeatRequest {
341
+ reservation_token: string;
342
+ state?: Extract<MultiplayerSessionState, 'reserved' | 'active'>;
343
+ ttl_minutes?: number;
344
+ }
345
+
346
+ export interface MultiplayerSessionReleaseRequest {
347
+ reservation_token: string;
348
+ }
349
+
350
+ export interface MultiplayerIssueAuthTicketRequest {
351
+ player_id?: string;
352
+ remote_identity?: string;
353
+ ttl_minutes?: number;
354
+ }
355
+
356
+ export interface MultiplayerIssueAuthTicketResponse {
357
+ ticket: MultiplayerAuthTicket;
358
+ auth_ticket: string;
359
+ }
360
+
361
+ export interface MultiplayerValidateAuthTicketRequest {
362
+ auth_ticket: string;
363
+ remote_identity?: string;
364
+ consume?: boolean;
365
+ }
366
+
367
+ export interface MultiplayerValidateAuthTicketForServerRequest extends MultiplayerValidateAuthTicketRequest {
368
+ server_token: string;
369
+ }
370
+
371
+ export interface MultiplayerValidateAuthTicketResponse {
372
+ valid: boolean;
373
+ ticket: MultiplayerAuthTicket;
374
+ }
375
+
376
+ export interface MultiplayerFavoritesParams {
377
+ player_id?: string;
378
+ kind?: MultiplayerFavoriteKind;
379
+ }
380
+
381
+ export interface MultiplayerFavoriteRequest {
382
+ player_id?: string;
383
+ server_id?: string;
384
+ kind?: MultiplayerFavoriteKind;
385
+ name?: string;
386
+ host?: string;
387
+ game_port?: number;
388
+ query_port?: number;
389
+ metadata?: MultiplayerMetadata;
390
+ last_played_at?: string;
391
+ }
392
+
393
+ export interface MultiplayerDeleteFavoriteParams {
394
+ player_id?: string;
395
+ }
396
+
397
+ export interface MultiplayerDeleteFavoriteResponse {
398
+ deleted: boolean;
399
+ }
400
+
401
+ export interface MultiplayerVoiceRoomListParams {
402
+ lobby_id?: string;
403
+ server_id?: string;
404
+ provider?: MultiplayerVoiceProvider;
405
+ topology?: MultiplayerVoiceTopology;
406
+ state?: MultiplayerVoiceState;
407
+ region?: string;
408
+ limit?: number;
409
+ }
410
+
411
+ export interface MultiplayerCreateVoiceRoomRequest {
412
+ player_id?: string;
413
+ display_name?: string;
414
+ lobby_id?: string;
415
+ server_id?: string;
416
+ provider?: MultiplayerVoiceProvider;
417
+ topology?: MultiplayerVoiceTopology;
418
+ state?: MultiplayerVoiceState;
419
+ region?: string;
420
+ codec?: MultiplayerVoiceCodec;
421
+ sample_rate?: number;
422
+ bitrate?: number;
423
+ frame_duration_ms?: 10 | 20 | 40 | 60;
424
+ channels?: 1 | 2;
425
+ max_participants?: number;
426
+ recording_allowed?: boolean;
427
+ moderation_enabled?: boolean;
428
+ connection_config?: MultiplayerMetadata;
429
+ metadata?: MultiplayerMetadata;
430
+ ttl_minutes?: number;
431
+ expires_at?: string;
432
+ }
433
+
434
+ export interface MultiplayerUpdateVoiceRoomRequest {
435
+ player_id?: string;
436
+ state?: MultiplayerVoiceState;
437
+ max_participants?: number;
438
+ recording_allowed?: boolean;
439
+ moderation_enabled?: boolean;
440
+ connection_config?: MultiplayerMetadata;
441
+ metadata?: MultiplayerMetadata;
442
+ expires_at?: string;
443
+ }
444
+
445
+ export interface MultiplayerJoinVoiceRoomRequest {
446
+ player_id?: string;
447
+ display_name?: string;
448
+ metadata?: MultiplayerMetadata;
449
+ ttl_minutes?: number;
450
+ }
451
+
452
+ export interface MultiplayerVoiceRoomTokenResponse {
453
+ voice_room: MultiplayerVoiceRoom;
454
+ participant: MultiplayerVoiceParticipant;
455
+ voice_token: string;
456
+ }
457
+
458
+ export interface MultiplayerVoiceHeartbeatRequest {
459
+ voice_token: string;
460
+ muted?: boolean;
461
+ deafened?: boolean;
462
+ speaking?: boolean;
463
+ last_sequence?: number;
464
+ ttl_minutes?: number;
465
+ }
466
+
467
+ export interface MultiplayerVoiceLeaveRequest {
468
+ voice_token: string;
469
+ }
470
+
471
+ export interface MultiplayerVoicePacketRequest {
472
+ voice_token: string;
473
+ packet_type?: MultiplayerVoicePacketType;
474
+ payload: string;
475
+ duration_ms?: number;
476
+ }
477
+
478
+ export interface MultiplayerVoicePollRequest {
479
+ voice_token: string;
480
+ after_sequence?: number;
481
+ limit?: number;
482
+ exclude_self?: boolean;
483
+ }
484
+
485
+ /**
486
+ * Steam-style multiplayer APIs for Glitch titles.
487
+ *
488
+ * The multiplayer surface is split into three groups:
489
+ * lobby coordination, voice coordination, server browser/reservations, and short-lived auth tickets.
490
+ * User JWTs can infer the player from the authenticated user. Title-token clients
491
+ * and game clients without a Glitch user session should pass a stable `player_id`.
492
+ * Dedicated servers use `server_token` on heartbeat and server-side ticket validation
493
+ * so they do not need to hold a user JWT or title token.
494
+ *
495
+ * These endpoints are intentionally database-agnostic from the SDK's point of view:
496
+ * callers work with public identifiers, metadata objects, and lifecycle events,
497
+ * while the backend owns how those records are stored.
498
+ */
499
+ class Multiplayer {
500
+
501
+ /**
502
+ * Search joinable, non-expired lobbies for a title.
503
+ *
504
+ * Filters are exact-match except `skill_band`, which the backend can use for
505
+ * near sorting. Default results exclude full, closed, unjoinable, and expired
506
+ * lobbies. Lifecycle context: clients usually call this before `joinLobby`;
507
+ * joins create a `lobby.joined` event on the backend.
508
+ *
509
+ * @param title_id Title UUID.
510
+ * @param params Optional filters such as region, game mode, map, lobby type, skill band, and limit.
511
+ * @example
512
+ * Multiplayer.searchLobbies('title-uuid', {
513
+ * region: 'us-central',
514
+ * game_mode: 'ranked_duos',
515
+ * skill_band: 1840,
516
+ * limit: 25
517
+ * });
518
+ */
519
+ public static searchLobbies<T = MultiplayerLobby[]>(title_id: string, params?: MultiplayerLobbySearchParams): AxiosPromise<Response<T>> {
520
+ return Requests.processRoute(MultiplayerRoute.routes.searchLobbies, undefined, { title_id }, params);
521
+ }
522
+
523
+ /**
524
+ * Create a lobby and insert the owner as the first joined member.
525
+ *
526
+ * Use this when matchmaking has no suitable lobby, when a player invites
527
+ * friends, or when a party needs pre-game setup before server assignment.
528
+ * Lifecycle events: `lobby.created`, then `lobby.joined` for the owner.
529
+ *
530
+ * @param title_id Title UUID.
531
+ * @param data Lobby configuration and optional owner/member metadata.
532
+ * @example
533
+ * Multiplayer.createLobby('title-uuid', {
534
+ * player_id: 'steam:76561198000000000',
535
+ * display_name: 'CinderAce',
536
+ * lobby_type: 'public',
537
+ * max_members: 4,
538
+ * region: 'us-central',
539
+ * game_mode: 'ranked_duos',
540
+ * metadata: { playlist: 'ranked', allow_voice: true }
541
+ * });
542
+ */
543
+ public static createLobby<T = MultiplayerLobby>(title_id: string, data: MultiplayerCreateLobbyRequest): AxiosPromise<Response<T>> {
544
+ return Requests.processRoute(MultiplayerRoute.routes.createLobby, data, { title_id });
545
+ }
546
+
547
+ /**
548
+ * Retrieve a lobby with members and assigned server information when present.
549
+ *
550
+ * Call this after lobby lifecycle notifications such as `lobby.joined`,
551
+ * `lobby.updated`, `lobby.owner_transferred`, or `lobby.server_assigned`.
552
+ *
553
+ * @param title_id Title UUID.
554
+ * @param lobby_id Lobby UUID.
555
+ */
556
+ public static showLobby<T = MultiplayerLobby>(title_id: string, lobby_id: string): AxiosPromise<Response<T>> {
557
+ return Requests.processRoute(MultiplayerRoute.routes.showLobby, undefined, { title_id, lobby_id });
558
+ }
559
+
560
+ /**
561
+ * Join a lobby or refresh an existing membership.
562
+ *
563
+ * This call is idempotent for a player already in the lobby and can update
564
+ * display name, ready state, or member metadata. It returns 409 when the lobby
565
+ * is full, closed, expired, or not joinable. Lifecycle event: `lobby.joined`.
566
+ *
567
+ * @param title_id Title UUID.
568
+ * @param lobby_id Lobby UUID.
569
+ * @param data Player identity and optional member metadata.
570
+ * @example
571
+ * Multiplayer.joinLobby('title-uuid', 'lobby-uuid', {
572
+ * player_id: 'steam:76561198000000001',
573
+ * display_name: 'Nova',
574
+ * ready: false,
575
+ * member_data: { character: 'Ash', rank: 1799 }
576
+ * });
577
+ */
578
+ public static joinLobby<T = MultiplayerLobbyMember>(title_id: string, lobby_id: string, data: MultiplayerJoinLobbyRequest): AxiosPromise<Response<T>> {
579
+ return Requests.processRoute(MultiplayerRoute.routes.joinLobby, data, { title_id, lobby_id });
580
+ }
581
+
582
+ /**
583
+ * Leave a lobby.
584
+ *
585
+ * If the owner leaves, ownership transfers to the oldest remaining joined
586
+ * member. If no members remain, the lobby closes. Lifecycle events:
587
+ * `lobby.left`, optionally `lobby.owner_transferred` or `lobby.updated`.
588
+ *
589
+ * @param title_id Title UUID.
590
+ * @param lobby_id Lobby UUID.
591
+ * @param data Optional player_id for title-token clients.
592
+ */
593
+ public static leaveLobby<T = MultiplayerLobbyMember | null>(title_id: string, lobby_id: string, data?: MultiplayerLeaveLobbyRequest): AxiosPromise<Response<T>> {
594
+ return Requests.processRoute(MultiplayerRoute.routes.leaveLobby, data, { title_id, lobby_id });
595
+ }
596
+
597
+ /**
598
+ * Update lobby metadata, visibility, joinability, limits, or state.
599
+ *
600
+ * This is owner-only. `max_members` cannot be lower than the current member
601
+ * count. Keep metadata low-frequency and mostly search/display oriented.
602
+ * Lifecycle event: `lobby.updated`.
603
+ *
604
+ * @param title_id Title UUID.
605
+ * @param lobby_id Lobby UUID.
606
+ * @param data Owner identity plus fields to update.
607
+ */
608
+ public static updateLobby<T = MultiplayerLobby>(title_id: string, lobby_id: string, data: MultiplayerUpdateLobbyRequest): AxiosPromise<Response<T>> {
609
+ return Requests.processRoute(MultiplayerRoute.routes.updateLobby, data, { title_id, lobby_id });
610
+ }
611
+
612
+ /**
613
+ * Assign a registered game server to a lobby.
614
+ *
615
+ * This owner-only handoff mirrors Steam's SetLobbyGameServer flow. Clients
616
+ * should react by reserving or connecting to the assigned server, then
617
+ * optionally leaving the lobby. Lifecycle event: `lobby.server_assigned`.
618
+ *
619
+ * @param title_id Title UUID.
620
+ * @param lobby_id Lobby UUID.
621
+ * @param data Server UUID and optional lobby state/joinability updates.
622
+ */
623
+ public static setLobbyServer<T = MultiplayerLobby>(title_id: string, lobby_id: string, data: MultiplayerSetLobbyServerRequest): AxiosPromise<Response<T>> {
624
+ return Requests.processRoute(MultiplayerRoute.routes.setLobbyServer, data, { title_id, lobby_id });
625
+ }
626
+
627
+ /**
628
+ * List ordered low-bandwidth lobby messages.
629
+ *
630
+ * Use `after_sequence` to poll for messages missed during reconnects or after
631
+ * a realtime `lobby.message_sent` event. This channel is for chat and control
632
+ * messages, not gameplay, positional data, or voice streaming.
633
+ *
634
+ * @param title_id Title UUID.
635
+ * @param lobby_id Lobby UUID.
636
+ * @param params Optional sequence cursor and limit.
637
+ */
638
+ public static listLobbyMessages<T = MultiplayerLobbyMessage[]>(title_id: string, lobby_id: string, params?: MultiplayerLobbyMessagesParams): AxiosPromise<Response<T>> {
639
+ return Requests.processRoute(MultiplayerRoute.routes.listLobbyMessages, undefined, { title_id, lobby_id }, params);
640
+ }
641
+
642
+ /**
643
+ * Send a low-bandwidth message to all lobby members.
644
+ *
645
+ * Payloads are capped at 4KB by the backend. Use this for chat, ready signals,
646
+ * invite/kick control messages, and owner-arbitrated choices. Lifecycle event:
647
+ * `lobby.message_sent`.
648
+ *
649
+ * @param title_id Title UUID.
650
+ * @param lobby_id Lobby UUID.
651
+ * @param data Message type, sender identity, and JSON payload.
652
+ * @example
653
+ * Multiplayer.sendLobbyMessage('title-uuid', 'lobby-uuid', {
654
+ * player_id: 'steam:76561198000000000',
655
+ * message_type: 'ready',
656
+ * payload: { ready: true }
657
+ * });
658
+ */
659
+ public static sendLobbyMessage<T = MultiplayerLobbyMessage>(title_id: string, lobby_id: string, data: MultiplayerSendLobbyMessageRequest): AxiosPromise<Response<T>> {
660
+ return Requests.processRoute(MultiplayerRoute.routes.sendLobbyMessage, data, { title_id, lobby_id });
661
+ }
662
+
663
+ /**
664
+ * List active/non-expired voice rooms for a title.
665
+ *
666
+ * Rooms can be attached to a lobby, a server, a party, or a proximity group.
667
+ * Use this to discover existing voice state before joining. Lifecycle context:
668
+ * realtime transports should mirror `voice.room_created`, `voice.room_updated`,
669
+ * `voice.joined`, and `voice.left`.
670
+ *
671
+ * @param title_id Title UUID.
672
+ * @param params Optional room filters such as lobby_id, server_id, provider, topology, state, region, and limit.
673
+ */
674
+ public static listVoiceRooms<T = MultiplayerVoiceRoom[]>(title_id: string, params?: MultiplayerVoiceRoomListParams): AxiosPromise<Response<T>> {
675
+ return Requests.processRoute(MultiplayerRoute.routes.listVoiceRooms, undefined, { title_id }, params);
676
+ }
677
+
678
+ /**
679
+ * Create a voice room and join the creator as the first participant.
680
+ *
681
+ * The backend returns `voice_token` once. Keep it client-side and use it for
682
+ * voice heartbeat, packet send, packet polling, and leave calls. `glitch_relay`
683
+ * can carry base64 Opus frames for prototypes, small-party fallback, or
684
+ * signaling. For production-scale audio, set `provider: 'external'` and reuse
685
+ * the room/token contract with WebRTC, an SFU, Vivox, Steam Networking, or an
686
+ * engine-native transport. Lifecycle events: `voice.room_created`,
687
+ * `voice.joined`.
688
+ *
689
+ * @param title_id Title UUID.
690
+ * @param data Voice codec, topology, linked lobby/server, and owner metadata.
691
+ * @example
692
+ * const { data } = await Multiplayer.createVoiceRoom('title-uuid', {
693
+ * player_id: 'steam:76561198000000000',
694
+ * display_name: 'CinderAce',
695
+ * lobby_id: 'lobby-uuid',
696
+ * provider: 'glitch_relay',
697
+ * topology: 'lobby',
698
+ * codec: 'opus',
699
+ * sample_rate: 48000,
700
+ * frame_duration_ms: 20,
701
+ * channels: 1,
702
+ * metadata: { push_to_talk: true }
703
+ * });
704
+ */
705
+ public static createVoiceRoom<T = MultiplayerVoiceRoomTokenResponse>(title_id: string, data: MultiplayerCreateVoiceRoomRequest): AxiosPromise<Response<T>> {
706
+ return Requests.processRoute(MultiplayerRoute.routes.createVoiceRoom, data, { title_id });
707
+ }
708
+
709
+ /**
710
+ * Retrieve a voice room with participant media states.
711
+ *
712
+ * Use this after `voice.joined`, `voice.heartbeat`, `voice.left`, or
713
+ * `voice.room_updated` to refresh in-game UI such as speaker lists, mute
714
+ * icons, or team voice controls.
715
+ *
716
+ * @param title_id Title UUID.
717
+ * @param voice_room_id Voice room UUID.
718
+ */
719
+ public static showVoiceRoom<T = MultiplayerVoiceRoom>(title_id: string, voice_room_id: string): AxiosPromise<Response<T>> {
720
+ return Requests.processRoute(MultiplayerRoute.routes.showVoiceRoom, undefined, { title_id, voice_room_id });
721
+ }
722
+
723
+ /**
724
+ * Update owner-controlled voice room state.
725
+ *
726
+ * Owner-only. Use this to close a room, adjust capacity, update moderation
727
+ * flags, or provide external provider connection details. The backend rejects
728
+ * lowering `max_participants` below the current participant count. Lifecycle
729
+ * event: `voice.room_updated`.
730
+ *
731
+ * @param title_id Title UUID.
732
+ * @param voice_room_id Voice room UUID.
733
+ * @param data Owner player identity and room fields to update.
734
+ */
735
+ public static updateVoiceRoom<T = MultiplayerVoiceRoom>(title_id: string, voice_room_id: string, data: MultiplayerUpdateVoiceRoomRequest): AxiosPromise<Response<T>> {
736
+ return Requests.processRoute(MultiplayerRoute.routes.updateVoiceRoom, data, { title_id, voice_room_id });
737
+ }
738
+
739
+ /**
740
+ * Join a voice room and receive a participant-scoped token.
741
+ *
742
+ * Rejoining with the same player is idempotent and rotates the token. The
743
+ * token is used by participant endpoints instead of requiring a user JWT or
744
+ * title token on every media request. Returns 409 when the room is closed,
745
+ * expired, or full. Lifecycle event: `voice.joined`.
746
+ *
747
+ * @param title_id Title UUID.
748
+ * @param voice_room_id Voice room UUID.
749
+ * @param data Player identity, display name, metadata, and token TTL.
750
+ */
751
+ public static joinVoiceRoom<T = MultiplayerVoiceRoomTokenResponse>(title_id: string, voice_room_id: string, data: MultiplayerJoinVoiceRoomRequest): AxiosPromise<Response<T>> {
752
+ return Requests.processRoute(MultiplayerRoute.routes.joinVoiceRoom, data, { title_id, voice_room_id });
753
+ }
754
+
755
+ /**
756
+ * Heartbeat voice participant state.
757
+ *
758
+ * Call every 10-30 seconds and whenever mute/deafen/speaking state changes.
759
+ * `last_sequence` tells the backend how far this participant has processed
760
+ * ordered packets. Expired participants are rejected with 409. Lifecycle event:
761
+ * `voice.heartbeat`.
762
+ *
763
+ * @param data Participant voice token and mutable media state.
764
+ */
765
+ public static heartbeatVoice<T = MultiplayerVoiceParticipant>(data: MultiplayerVoiceHeartbeatRequest): AxiosPromise<Response<T>> {
766
+ return Requests.processRoute(MultiplayerRoute.routes.heartbeatVoice, data);
767
+ }
768
+
769
+ /**
770
+ * Leave the current voice room for a participant token.
771
+ *
772
+ * This is idempotent for disconnect cleanup: room participant count is
773
+ * decremented once, room ownership is transferred when possible, and an
774
+ * empty room closes. The token remains valid only for retrying this leave
775
+ * call; heartbeat, send, and poll calls reject left participants. Lifecycle
776
+ * event: `voice.left`.
777
+ *
778
+ * @param data Participant voice token.
779
+ */
780
+ public static leaveVoice<T = MultiplayerVoiceParticipant>(data: MultiplayerVoiceLeaveRequest): AxiosPromise<Response<T>> {
781
+ return Requests.processRoute(MultiplayerRoute.routes.leaveVoice, data);
782
+ }
783
+
784
+ /**
785
+ * Send one ordered voice-room packet.
786
+ *
787
+ * `audio` packets should contain compact compressed frames such as base64 Opus
788
+ * at 48kHz mono/20ms. `offer`, `answer`, and `ice` packets support WebRTC
789
+ * signaling. `control`, `speaking`, and `mute_state` packets are for custom
790
+ * engine state. Audio payloads are capped at 16KB; non-audio packets at 4KB.
791
+ * Muted participants cannot send audio. Lifecycle event: `voice.packet_sent`.
792
+ *
793
+ * @param data Participant token, packet type, payload, and optional duration.
794
+ * @example
795
+ * await Multiplayer.sendVoicePacket({
796
+ * voice_token: voiceToken,
797
+ * packet_type: 'audio',
798
+ * payload: base64OpusFrame,
799
+ * duration_ms: 20
800
+ * });
801
+ */
802
+ public static sendVoicePacket<T = MultiplayerVoicePacket>(data: MultiplayerVoicePacketRequest): AxiosPromise<Response<T>> {
803
+ return Requests.processRoute(MultiplayerRoute.routes.sendVoicePacket, data);
804
+ }
805
+
806
+ /**
807
+ * Poll ordered voice-room packets after a known sequence.
808
+ *
809
+ * Defaults to excluding packets sent by the caller. Use the highest returned
810
+ * sequence as the next `after_sequence` cursor. This is useful for fallback
811
+ * relay, WebRTC signaling, reconnect recovery, and small-party prototypes.
812
+ * Lifecycle event: `voice.packet_polled`.
813
+ *
814
+ * @param data Participant token, optional sequence cursor, limit, and self-exclusion flag.
815
+ */
816
+ public static pollVoicePackets<T = MultiplayerVoicePacket[]>(data: MultiplayerVoicePollRequest): AxiosPromise<Response<T>> {
817
+ return Requests.processRoute(MultiplayerRoute.routes.pollVoicePackets, data);
818
+ }
819
+
820
+ /**
821
+ * Browse public, joinable multiplayer servers for a title.
822
+ *
823
+ * Default results exclude private, draining, offline, stale, expired, and full
824
+ * servers. Title administrators can pass `include_private` to inspect servers
825
+ * that normal clients cannot join.
826
+ *
827
+ * @param title_id Title UUID.
828
+ * @param params Optional server browser filters.
829
+ */
830
+ public static browseServers<T = MultiplayerServer[]>(title_id: string, params?: MultiplayerServerBrowserParams): AxiosPromise<Response<T>> {
831
+ return Requests.processRoute(MultiplayerRoute.routes.browseServers, undefined, { title_id }, params);
832
+ }
833
+
834
+ /**
835
+ * Register or refresh a multiplayer server and receive a one-time server token.
836
+ *
837
+ * Store `server_token` only on the server process. The backend stores only a
838
+ * hash and will not return the plain token again. Counts are validated so
839
+ * `current_players + bot_players` cannot exceed `max_players`. Lifecycle event:
840
+ * `server.registered`.
841
+ *
842
+ * @param title_id Title UUID.
843
+ * @param data Server browser, connection, rule, and capacity metadata.
844
+ * @example
845
+ * Multiplayer.registerServer('title-uuid', {
846
+ * name: 'Ranked US Central 01',
847
+ * server_type: 'dedicated',
848
+ * status: 'active',
849
+ * host: '203.0.113.42',
850
+ * game_port: 7777,
851
+ * query_port: 27015,
852
+ * transport: 'udp',
853
+ * max_players: 16,
854
+ * secure: true,
855
+ * tags: ['ranked', 'duos']
856
+ * });
857
+ */
858
+ public static registerServer<T = MultiplayerRegisterServerResponse>(title_id: string, data: MultiplayerRegisterServerRequest): AxiosPromise<Response<T>> {
859
+ return Requests.processRoute(MultiplayerRoute.routes.registerServer, data, { title_id });
860
+ }
861
+
862
+ /**
863
+ * Heartbeat a multiplayer server with its dedicated `server_token`.
864
+ *
865
+ * Call every 30-60 seconds and whenever player counts, rules, or metadata
866
+ * change. Stale servers are hidden from default browsing and reservation.
867
+ * This endpoint is for dedicated/listen server processes and does not require
868
+ * a user JWT. Lifecycle event: `server.heartbeat`.
869
+ *
870
+ * @param title_id Title UUID.
871
+ * @param server_id Server UUID.
872
+ * @param data Server token and optional mutable server state.
873
+ */
874
+ public static heartbeatServer<T = MultiplayerServer>(title_id: string, server_id: string, data: MultiplayerServerHeartbeatRequest): AxiosPromise<Response<T>> {
875
+ return Requests.processRoute(MultiplayerRoute.routes.heartbeatServer, data, { title_id, server_id });
876
+ }
877
+
878
+ /**
879
+ * Reserve a short-lived slot on a multiplayer server before connecting.
880
+ *
881
+ * Reservations protect capacity during game handoff. The backend rejects stale,
882
+ * private, full, draining, offline, expired, or duplicate open reservations.
883
+ * The plain `reservation_token` is returned once and is used for session
884
+ * heartbeat/release calls. Lifecycle event: `server.reserved`.
885
+ *
886
+ * @param title_id Title UUID.
887
+ * @param server_id Server UUID.
888
+ * @param data Optional player/lobby identity and reservation TTL.
889
+ */
890
+ public static reserveServer<T = MultiplayerReserveServerResponse>(title_id: string, server_id: string, data?: MultiplayerReserveServerRequest): AxiosPromise<Response<T>> {
891
+ return Requests.processRoute(MultiplayerRoute.routes.reserveServer, data, { title_id, server_id });
892
+ }
893
+
894
+ /**
895
+ * Heartbeat an open multiplayer session reservation.
896
+ *
897
+ * Use this after a successful reservation while the client is connecting or
898
+ * playing. Expired sessions are marked expired and capacity is recovered before
899
+ * the backend returns 409. Lifecycle events: `session.heartbeat` or
900
+ * `session.expired`.
901
+ *
902
+ * @param data Reservation token and optional state/TTL.
903
+ */
904
+ public static heartbeatSession<T = MultiplayerSession>(data: MultiplayerSessionHeartbeatRequest): AxiosPromise<Response<T>> {
905
+ return Requests.processRoute(MultiplayerRoute.routes.heartbeatSession, data);
906
+ }
907
+
908
+ /**
909
+ * Release an open multiplayer session reservation.
910
+ *
911
+ * Call this on normal disconnect, failed connection attempts, or shutdown so
912
+ * server capacity is decremented promptly. The backend makes release safe to
913
+ * call more than once for an already closed reservation. Lifecycle event:
914
+ * `session.released`.
915
+ *
916
+ * @param data Reservation token returned by `reserveServer`.
917
+ */
918
+ public static releaseSession<T = MultiplayerSession>(data: MultiplayerSessionReleaseRequest): AxiosPromise<Response<T>> {
919
+ return Requests.processRoute(MultiplayerRoute.routes.releaseSession, data);
920
+ }
921
+
922
+ /**
923
+ * Issue a short-lived multiplayer auth ticket for a player.
924
+ *
925
+ * The plain `auth_ticket` is returned once and only a hash is stored by the
926
+ * backend. Use this for P2P or dedicated-server admission before game traffic
927
+ * begins. `remote_identity` can bind the ticket to a server or validator.
928
+ * Lifecycle event: `auth_ticket.issued`.
929
+ *
930
+ * @param title_id Title UUID.
931
+ * @param data Player identity, optional remote identity, and TTL.
932
+ */
933
+ public static issueAuthTicket<T = MultiplayerIssueAuthTicketResponse>(title_id: string, data?: MultiplayerIssueAuthTicketRequest): AxiosPromise<Response<T>> {
934
+ return Requests.processRoute(MultiplayerRoute.routes.issueAuthTicket, data, { title_id });
935
+ }
936
+
937
+ /**
938
+ * Validate a multiplayer auth ticket from a trusted title/user context.
939
+ *
940
+ * Pass `consume: true` for one-time tickets to prevent replay. Dedicated
941
+ * servers should usually call `validateAuthTicketForServer` so they can use
942
+ * `server_token` instead of a title token or user JWT. Lifecycle event:
943
+ * `auth_ticket.validated`.
944
+ *
945
+ * @param title_id Title UUID.
946
+ * @param data Ticket, optional remote identity check, and consume flag.
947
+ */
948
+ public static validateAuthTicket<T = MultiplayerValidateAuthTicketResponse>(title_id: string, data: MultiplayerValidateAuthTicketRequest): AxiosPromise<Response<T>> {
949
+ return Requests.processRoute(MultiplayerRoute.routes.validateAuthTicket, data, { title_id });
950
+ }
951
+
952
+ /**
953
+ * Validate an auth ticket as a dedicated server.
954
+ *
955
+ * This server-token endpoint lets a dedicated server admit players without
956
+ * holding a user JWT or title token. Pass `consume: true` to prevent replay.
957
+ * Lifecycle event: `auth_ticket.validated`.
958
+ *
959
+ * @param title_id Title UUID.
960
+ * @param server_id Server UUID.
961
+ * @param data Server token, player auth ticket, optional remote identity, and consume flag.
962
+ */
963
+ public static validateAuthTicketForServer<T = MultiplayerValidateAuthTicketResponse>(title_id: string, server_id: string, data: MultiplayerValidateAuthTicketForServerRequest): AxiosPromise<Response<T>> {
964
+ return Requests.processRoute(MultiplayerRoute.routes.validateAuthTicketForServer, data, { title_id, server_id });
965
+ }
966
+
967
+ /**
968
+ * List a player's server favorites or history entries.
969
+ *
970
+ * Use this for Steam-like favorites and recent servers tabs. Title-token
971
+ * clients should pass `player_id`; user JWT clients default to the user UUID.
972
+ *
973
+ * @param title_id Title UUID.
974
+ * @param params Optional player and favorite/history filter.
975
+ */
976
+ public static listFavorites<T = MultiplayerServerFavorite[]>(title_id: string, params?: MultiplayerFavoritesParams): AxiosPromise<Response<T>> {
977
+ return Requests.processRoute(MultiplayerRoute.routes.listFavorites, undefined, { title_id }, params);
978
+ }
979
+
980
+ /**
981
+ * Add or update a favorite/history server entry for a player.
982
+ *
983
+ * Provide `server_id` for a registered Glitch server, or `host` plus
984
+ * `game_port` for a direct/community server. Lifecycle event:
985
+ * `favorite.upserted`.
986
+ *
987
+ * @param title_id Title UUID.
988
+ * @param data Favorite/history target and optional metadata.
989
+ */
990
+ public static addFavorite<T = MultiplayerServerFavorite>(title_id: string, data: MultiplayerFavoriteRequest): AxiosPromise<Response<T>> {
991
+ return Requests.processRoute(MultiplayerRoute.routes.addFavorite, data, { title_id });
992
+ }
993
+
994
+ /**
995
+ * Delete a player's favorite/history server entry.
996
+ *
997
+ * The SDK sends optional `player_id` as a query parameter because the shared
998
+ * request helper treats DELETE payloads as query params. This maps cleanly to
999
+ * the backend's optional player identity validation for title-token clients.
1000
+ * Lifecycle event: `favorite.deleted`.
1001
+ *
1002
+ * @param title_id Title UUID.
1003
+ * @param favorite_id Favorite/history UUID.
1004
+ * @param params Optional player_id for title-token clients.
1005
+ */
1006
+ public static deleteFavorite<T = MultiplayerDeleteFavoriteResponse>(title_id: string, favorite_id: string, params?: MultiplayerDeleteFavoriteParams): AxiosPromise<Response<T>> {
1007
+ return Requests.processRoute(MultiplayerRoute.routes.deleteFavorite, undefined, { title_id, favorite_id }, params);
1008
+ }
1009
+
1010
+ }
1011
+
1012
+ export default Multiplayer;