@remix-gg/sdk 0.9.0 → 0.10.0

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/dist/index.d.mts CHANGED
@@ -1,9 +1,101 @@
1
+ /**
2
+ * `sdk.realtime` — realtime multiplayer rooms for Remix Desktop games.
3
+ *
4
+ * The game sees rooms, peers, and messages; everything else is deliberately
5
+ * invisible. The platform's room broker (Remix Desktop main) owns identity,
6
+ * room admission, signaling, and ICE minting; this controller rides the
7
+ * `multiplayer_*` game events both ways and drives the data-channel mesh
8
+ * (`./mesh.ts`) under the surface. A game never touches `RTCPeerConnection`,
9
+ * SDP, or ICE — which is what lets the transport change under a published
10
+ * game without the game updating.
11
+ *
12
+ * Membership truth is the SERVER ROSTER the broker relays
13
+ * (`multiplayer_peers`), never the mesh: a rebuilt slot re-fires its
14
+ * transport callbacks and a repeated bye can announce the same departure
15
+ * twice, so peer join/leave here is a roster diff and nothing else. The
16
+ * mesh's own callbacks are diagnostics.
17
+ */
18
+ type RealtimePeer = {
19
+ userId: string;
20
+ username: string;
21
+ pfp: string | null;
22
+ /** ISO seat stamp; seat order (oldest first) is the deterministic tiebreak. */
23
+ joinedAt: string | null;
24
+ };
25
+ type RealtimeEndReason = 'left' | 'expired' | 'offline' | 'closed';
26
+ type RealtimeErrorCode = 'room_not_found' | 'room_full' | 'game_mismatch' | 'no_game_id' | 'join_failed';
27
+ declare class RealtimeRoomError extends Error {
28
+ readonly code: RealtimeErrorCode;
29
+ constructor(code: RealtimeErrorCode, message: string);
30
+ }
31
+ type RealtimeSendOptions = {
32
+ /**
33
+ * Default true: ordered, retransmitted — lobby, countdowns, results.
34
+ * `reliable: false` is the per-frame state stream (unordered, never
35
+ * retransmitted): a packet that arrives late is worth less than nothing,
36
+ * and one that never arrives is replaced by the next frame anyway.
37
+ */
38
+ reliable?: boolean;
39
+ };
40
+ interface RealtimeRoom {
41
+ readonly roomId: string;
42
+ /** The join code — what the host shares with friends. */
43
+ readonly code: string;
44
+ readonly selfId: string;
45
+ readonly hostUserId: string;
46
+ /** Whether this player created the room. */
47
+ readonly isHost: boolean;
48
+ /** The live roster, self excluded, in seat order. */
49
+ readonly peers: RealtimePeer[];
50
+ readonly ended: boolean;
51
+ /** Broadcast to every peer. Data must be JSON-serializable. */
52
+ send(data: unknown, options?: RealtimeSendOptions): void;
53
+ sendTo(userId: string, data: unknown, options?: RealtimeSendOptions): void;
54
+ onMessage(callback: (fromUserId: string, data: unknown) => void): () => void;
55
+ onPeerJoin(callback: (peer: RealtimePeer) => void): () => void;
56
+ onPeerLeave(callback: (peer: RealtimePeer) => void): () => void;
57
+ onEnded(callback: (reason: RealtimeEndReason) => void): () => void;
58
+ /** Transport trouble worth telling the player about; the room keeps trying. */
59
+ onError(callback: (message: string) => void): () => void;
60
+ /**
61
+ * Open the platform's invite share flow — the friend picker that sends the
62
+ * room's join code into DMs and groups. The game never sees the friend
63
+ * list; the platform owns the whole exchange. A game may also just show
64
+ * `room.code` for players to share by hand.
65
+ */
66
+ invite(): void;
67
+ leave(): void;
68
+ }
69
+ interface RealtimeNamespace {
70
+ /** Create a room for this game and take the first seat. One live room at a time. */
71
+ createRoom(): Promise<RealtimeRoom>;
72
+ /** Join a friend's room by its invite code. */
73
+ joinRoom(code: string): Promise<RealtimeRoom>;
74
+ /** The live room, or null. */
75
+ readonly room: RealtimeRoom | null;
76
+ /**
77
+ * Every room that becomes live — including one the PLATFORM joined for the
78
+ * player (accepting an invite boots the game already seated). A lobby
79
+ * screen should mount from here, not only from its own create/join call.
80
+ */
81
+ onRoom(callback: (room: RealtimeRoom) => void): () => void;
82
+ /**
83
+ * A room the platform tried to join for the player (an accepted invite)
84
+ * that failed — full, expired, or the wrong game. There is no pending
85
+ * `createRoom`/`joinRoom` call to reject, so this is where the failure
86
+ * surfaces; a lobby should tell the player rather than sit solo as if
87
+ * nothing happened. Requested joins keep rejecting their own promise.
88
+ */
89
+ onRoomError(callback: (error: RealtimeRoomError) => void): () => void;
90
+ }
91
+
1
92
  declare global {
2
93
  interface Window {
3
94
  FarcadeSDK: typeof sdk;
4
95
  RemixSDK: typeof sdk;
5
96
  }
6
97
  }
98
+
7
99
  type ViewContext = 'feed' | 'full_screen' | 'challenge' | 'tournament';
8
100
  type SafeAreaInset = {
9
101
  top: number;
@@ -175,7 +267,89 @@ type PurchaseCompleteEvent = {
175
267
  item?: string;
176
268
  };
177
269
  };
178
- type GameEvent = PlayAgainEvent | SinglePlayerGameOverEvent | ReadyEvent | HapticFeedbackEvent | ToggleMuteEvent | GameErrorEvent | SaveGameStateEvent | RefuteGameStateEvent | GameInfoEvent | GameStateUpdatedEvent | MultiplayerGameOverEvent | MultiplayerSaveGameStateEvent | PurchaseEvent | PurchaseCompleteEvent;
270
+ type RealtimeSignalKind = 'offer' | 'answer' | 'ice' | 'bye';
271
+ type MultiplayerCreateRoomEvent = {
272
+ type: 'multiplayer_create_room';
273
+ data: undefined;
274
+ };
275
+ type MultiplayerJoinRoomEvent = {
276
+ type: 'multiplayer_join_room';
277
+ data: {
278
+ code: string;
279
+ };
280
+ };
281
+ type MultiplayerLeaveRoomEvent = {
282
+ type: 'multiplayer_leave_room';
283
+ data: undefined;
284
+ };
285
+ type MultiplayerSignalEvent = {
286
+ type: 'multiplayer_signal';
287
+ data: {
288
+ toUserId: string;
289
+ kind: RealtimeSignalKind;
290
+ payload: string;
291
+ };
292
+ };
293
+ type MultiplayerRefreshIceEvent = {
294
+ type: 'multiplayer_refresh_ice';
295
+ data: undefined;
296
+ };
297
+ /**
298
+ * Open the platform's invite share flow for the live room. Carries nothing:
299
+ * the platform already knows the room's game and code.
300
+ */
301
+ type MultiplayerRequestInviteEvent = {
302
+ type: 'multiplayer_request_invite';
303
+ data: undefined;
304
+ };
305
+ type MultiplayerSessionEvent = {
306
+ type: 'multiplayer_session';
307
+ data: {
308
+ roomId: string;
309
+ code: string;
310
+ gameId: string;
311
+ selfId: string;
312
+ hostUserId: string;
313
+ peers: RealtimePeer[];
314
+ iceServers: RTCIceServer[];
315
+ };
316
+ };
317
+ type MultiplayerPeersEvent = {
318
+ type: 'multiplayer_peers';
319
+ data: {
320
+ peers: RealtimePeer[];
321
+ };
322
+ };
323
+ type MultiplayerSignalsEvent = {
324
+ type: 'multiplayer_signals';
325
+ data: {
326
+ signals: Array<{
327
+ fromUserId: string;
328
+ kind: string;
329
+ payload: string;
330
+ }>;
331
+ };
332
+ };
333
+ type MultiplayerIceServersEvent = {
334
+ type: 'multiplayer_ice_servers';
335
+ data: {
336
+ iceServers: RTCIceServer[];
337
+ };
338
+ };
339
+ type MultiplayerSessionEndedEvent = {
340
+ type: 'multiplayer_session_ended';
341
+ data: {
342
+ reason: RealtimeEndReason;
343
+ };
344
+ };
345
+ type MultiplayerErrorEvent = {
346
+ type: 'multiplayer_error';
347
+ data: {
348
+ code: RealtimeErrorCode;
349
+ message: string;
350
+ };
351
+ };
352
+ type GameEvent = PlayAgainEvent | SinglePlayerGameOverEvent | ReadyEvent | HapticFeedbackEvent | ToggleMuteEvent | GameErrorEvent | SaveGameStateEvent | RefuteGameStateEvent | GameInfoEvent | GameStateUpdatedEvent | MultiplayerGameOverEvent | MultiplayerSaveGameStateEvent | PurchaseEvent | PurchaseCompleteEvent | MultiplayerCreateRoomEvent | MultiplayerJoinRoomEvent | MultiplayerLeaveRoomEvent | MultiplayerSignalEvent | MultiplayerRefreshIceEvent | MultiplayerRequestInviteEvent | MultiplayerSessionEvent | MultiplayerPeersEvent | MultiplayerSignalsEvent | MultiplayerIceServersEvent | MultiplayerSessionEndedEvent | MultiplayerErrorEvent;
179
353
  type GameEventMessage<T extends GameEvent['type']> = {
180
354
  type: 'game_event';
181
355
  event: Extract<GameEvent, {
@@ -185,8 +359,8 @@ type GameEventMessage<T extends GameEvent['type']> = {
185
359
  /**
186
360
  * Messages from the game host to the game client
187
361
  */
188
- type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
189
- type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase'>;
362
+ type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete' | 'multiplayer_session' | 'multiplayer_peers' | 'multiplayer_signals' | 'multiplayer_ice_servers' | 'multiplayer_session_ended' | 'multiplayer_error'>;
363
+ type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase' | 'multiplayer_create_room' | 'multiplayer_join_room' | 'multiplayer_leave_room' | 'multiplayer_signal' | 'multiplayer_refresh_ice' | 'multiplayer_request_invite'>;
190
364
  type EventCallback = (data: unknown) => void;
191
365
  declare class RemixSDK {
192
366
  /**
@@ -266,6 +440,15 @@ declare class RemixSDK {
266
440
  purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
267
441
  };
268
442
  };
443
+ /**
444
+ * Realtime multiplayer rooms (Remix Desktop): create or join a room, then
445
+ * talk to peers over `room.send` / `room.onMessage`. Distinct from
446
+ * `sdk.multiplayer`, which is the platform-arbitrated turn-based flow.
447
+ * The transport underneath is the platform's business; games see rooms,
448
+ * peers, and messages, and nothing else.
449
+ */
450
+ private realtimeController;
451
+ realtime: RealtimeNamespace;
269
452
  private emit;
270
453
  private handleMessage;
271
454
  private sendMessage;
@@ -275,4 +458,4 @@ declare class RemixSDK {
275
458
  }
276
459
  declare const sdk: RemixSDK;
277
460
 
278
- export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, type LevelProgressState, type LevelStars, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, sdk };
461
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, type LevelProgressState, type LevelStars, type MultiplayerCreateRoomEvent, type MultiplayerErrorEvent, type MultiplayerGameOverEvent, type MultiplayerIceServersEvent, type MultiplayerJoinRoomEvent, type MultiplayerLeaveRoomEvent, type MultiplayerPeersEvent, type MultiplayerRefreshIceEvent, type MultiplayerRequestInviteEvent, type MultiplayerSaveGameStateEvent, type MultiplayerSessionEndedEvent, type MultiplayerSessionEvent, type MultiplayerSignalEvent, type MultiplayerSignalsEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RealtimeEndReason, type RealtimeErrorCode, type RealtimeNamespace, type RealtimePeer, type RealtimeRoom, RealtimeRoomError, type RealtimeSendOptions, type RealtimeSignalKind, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, sdk };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,101 @@
1
+ /**
2
+ * `sdk.realtime` — realtime multiplayer rooms for Remix Desktop games.
3
+ *
4
+ * The game sees rooms, peers, and messages; everything else is deliberately
5
+ * invisible. The platform's room broker (Remix Desktop main) owns identity,
6
+ * room admission, signaling, and ICE minting; this controller rides the
7
+ * `multiplayer_*` game events both ways and drives the data-channel mesh
8
+ * (`./mesh.ts`) under the surface. A game never touches `RTCPeerConnection`,
9
+ * SDP, or ICE — which is what lets the transport change under a published
10
+ * game without the game updating.
11
+ *
12
+ * Membership truth is the SERVER ROSTER the broker relays
13
+ * (`multiplayer_peers`), never the mesh: a rebuilt slot re-fires its
14
+ * transport callbacks and a repeated bye can announce the same departure
15
+ * twice, so peer join/leave here is a roster diff and nothing else. The
16
+ * mesh's own callbacks are diagnostics.
17
+ */
18
+ type RealtimePeer = {
19
+ userId: string;
20
+ username: string;
21
+ pfp: string | null;
22
+ /** ISO seat stamp; seat order (oldest first) is the deterministic tiebreak. */
23
+ joinedAt: string | null;
24
+ };
25
+ type RealtimeEndReason = 'left' | 'expired' | 'offline' | 'closed';
26
+ type RealtimeErrorCode = 'room_not_found' | 'room_full' | 'game_mismatch' | 'no_game_id' | 'join_failed';
27
+ declare class RealtimeRoomError extends Error {
28
+ readonly code: RealtimeErrorCode;
29
+ constructor(code: RealtimeErrorCode, message: string);
30
+ }
31
+ type RealtimeSendOptions = {
32
+ /**
33
+ * Default true: ordered, retransmitted — lobby, countdowns, results.
34
+ * `reliable: false` is the per-frame state stream (unordered, never
35
+ * retransmitted): a packet that arrives late is worth less than nothing,
36
+ * and one that never arrives is replaced by the next frame anyway.
37
+ */
38
+ reliable?: boolean;
39
+ };
40
+ interface RealtimeRoom {
41
+ readonly roomId: string;
42
+ /** The join code — what the host shares with friends. */
43
+ readonly code: string;
44
+ readonly selfId: string;
45
+ readonly hostUserId: string;
46
+ /** Whether this player created the room. */
47
+ readonly isHost: boolean;
48
+ /** The live roster, self excluded, in seat order. */
49
+ readonly peers: RealtimePeer[];
50
+ readonly ended: boolean;
51
+ /** Broadcast to every peer. Data must be JSON-serializable. */
52
+ send(data: unknown, options?: RealtimeSendOptions): void;
53
+ sendTo(userId: string, data: unknown, options?: RealtimeSendOptions): void;
54
+ onMessage(callback: (fromUserId: string, data: unknown) => void): () => void;
55
+ onPeerJoin(callback: (peer: RealtimePeer) => void): () => void;
56
+ onPeerLeave(callback: (peer: RealtimePeer) => void): () => void;
57
+ onEnded(callback: (reason: RealtimeEndReason) => void): () => void;
58
+ /** Transport trouble worth telling the player about; the room keeps trying. */
59
+ onError(callback: (message: string) => void): () => void;
60
+ /**
61
+ * Open the platform's invite share flow — the friend picker that sends the
62
+ * room's join code into DMs and groups. The game never sees the friend
63
+ * list; the platform owns the whole exchange. A game may also just show
64
+ * `room.code` for players to share by hand.
65
+ */
66
+ invite(): void;
67
+ leave(): void;
68
+ }
69
+ interface RealtimeNamespace {
70
+ /** Create a room for this game and take the first seat. One live room at a time. */
71
+ createRoom(): Promise<RealtimeRoom>;
72
+ /** Join a friend's room by its invite code. */
73
+ joinRoom(code: string): Promise<RealtimeRoom>;
74
+ /** The live room, or null. */
75
+ readonly room: RealtimeRoom | null;
76
+ /**
77
+ * Every room that becomes live — including one the PLATFORM joined for the
78
+ * player (accepting an invite boots the game already seated). A lobby
79
+ * screen should mount from here, not only from its own create/join call.
80
+ */
81
+ onRoom(callback: (room: RealtimeRoom) => void): () => void;
82
+ /**
83
+ * A room the platform tried to join for the player (an accepted invite)
84
+ * that failed — full, expired, or the wrong game. There is no pending
85
+ * `createRoom`/`joinRoom` call to reject, so this is where the failure
86
+ * surfaces; a lobby should tell the player rather than sit solo as if
87
+ * nothing happened. Requested joins keep rejecting their own promise.
88
+ */
89
+ onRoomError(callback: (error: RealtimeRoomError) => void): () => void;
90
+ }
91
+
1
92
  declare global {
2
93
  interface Window {
3
94
  FarcadeSDK: typeof sdk;
4
95
  RemixSDK: typeof sdk;
5
96
  }
6
97
  }
98
+
7
99
  type ViewContext = 'feed' | 'full_screen' | 'challenge' | 'tournament';
8
100
  type SafeAreaInset = {
9
101
  top: number;
@@ -175,7 +267,89 @@ type PurchaseCompleteEvent = {
175
267
  item?: string;
176
268
  };
177
269
  };
178
- type GameEvent = PlayAgainEvent | SinglePlayerGameOverEvent | ReadyEvent | HapticFeedbackEvent | ToggleMuteEvent | GameErrorEvent | SaveGameStateEvent | RefuteGameStateEvent | GameInfoEvent | GameStateUpdatedEvent | MultiplayerGameOverEvent | MultiplayerSaveGameStateEvent | PurchaseEvent | PurchaseCompleteEvent;
270
+ type RealtimeSignalKind = 'offer' | 'answer' | 'ice' | 'bye';
271
+ type MultiplayerCreateRoomEvent = {
272
+ type: 'multiplayer_create_room';
273
+ data: undefined;
274
+ };
275
+ type MultiplayerJoinRoomEvent = {
276
+ type: 'multiplayer_join_room';
277
+ data: {
278
+ code: string;
279
+ };
280
+ };
281
+ type MultiplayerLeaveRoomEvent = {
282
+ type: 'multiplayer_leave_room';
283
+ data: undefined;
284
+ };
285
+ type MultiplayerSignalEvent = {
286
+ type: 'multiplayer_signal';
287
+ data: {
288
+ toUserId: string;
289
+ kind: RealtimeSignalKind;
290
+ payload: string;
291
+ };
292
+ };
293
+ type MultiplayerRefreshIceEvent = {
294
+ type: 'multiplayer_refresh_ice';
295
+ data: undefined;
296
+ };
297
+ /**
298
+ * Open the platform's invite share flow for the live room. Carries nothing:
299
+ * the platform already knows the room's game and code.
300
+ */
301
+ type MultiplayerRequestInviteEvent = {
302
+ type: 'multiplayer_request_invite';
303
+ data: undefined;
304
+ };
305
+ type MultiplayerSessionEvent = {
306
+ type: 'multiplayer_session';
307
+ data: {
308
+ roomId: string;
309
+ code: string;
310
+ gameId: string;
311
+ selfId: string;
312
+ hostUserId: string;
313
+ peers: RealtimePeer[];
314
+ iceServers: RTCIceServer[];
315
+ };
316
+ };
317
+ type MultiplayerPeersEvent = {
318
+ type: 'multiplayer_peers';
319
+ data: {
320
+ peers: RealtimePeer[];
321
+ };
322
+ };
323
+ type MultiplayerSignalsEvent = {
324
+ type: 'multiplayer_signals';
325
+ data: {
326
+ signals: Array<{
327
+ fromUserId: string;
328
+ kind: string;
329
+ payload: string;
330
+ }>;
331
+ };
332
+ };
333
+ type MultiplayerIceServersEvent = {
334
+ type: 'multiplayer_ice_servers';
335
+ data: {
336
+ iceServers: RTCIceServer[];
337
+ };
338
+ };
339
+ type MultiplayerSessionEndedEvent = {
340
+ type: 'multiplayer_session_ended';
341
+ data: {
342
+ reason: RealtimeEndReason;
343
+ };
344
+ };
345
+ type MultiplayerErrorEvent = {
346
+ type: 'multiplayer_error';
347
+ data: {
348
+ code: RealtimeErrorCode;
349
+ message: string;
350
+ };
351
+ };
352
+ type GameEvent = PlayAgainEvent | SinglePlayerGameOverEvent | ReadyEvent | HapticFeedbackEvent | ToggleMuteEvent | GameErrorEvent | SaveGameStateEvent | RefuteGameStateEvent | GameInfoEvent | GameStateUpdatedEvent | MultiplayerGameOverEvent | MultiplayerSaveGameStateEvent | PurchaseEvent | PurchaseCompleteEvent | MultiplayerCreateRoomEvent | MultiplayerJoinRoomEvent | MultiplayerLeaveRoomEvent | MultiplayerSignalEvent | MultiplayerRefreshIceEvent | MultiplayerRequestInviteEvent | MultiplayerSessionEvent | MultiplayerPeersEvent | MultiplayerSignalsEvent | MultiplayerIceServersEvent | MultiplayerSessionEndedEvent | MultiplayerErrorEvent;
179
353
  type GameEventMessage<T extends GameEvent['type']> = {
180
354
  type: 'game_event';
181
355
  event: Extract<GameEvent, {
@@ -185,8 +359,8 @@ type GameEventMessage<T extends GameEvent['type']> = {
185
359
  /**
186
360
  * Messages from the game host to the game client
187
361
  */
188
- type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
189
- type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase'>;
362
+ type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete' | 'multiplayer_session' | 'multiplayer_peers' | 'multiplayer_signals' | 'multiplayer_ice_servers' | 'multiplayer_session_ended' | 'multiplayer_error'>;
363
+ type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase' | 'multiplayer_create_room' | 'multiplayer_join_room' | 'multiplayer_leave_room' | 'multiplayer_signal' | 'multiplayer_refresh_ice' | 'multiplayer_request_invite'>;
190
364
  type EventCallback = (data: unknown) => void;
191
365
  declare class RemixSDK {
192
366
  /**
@@ -266,6 +440,15 @@ declare class RemixSDK {
266
440
  purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
267
441
  };
268
442
  };
443
+ /**
444
+ * Realtime multiplayer rooms (Remix Desktop): create or join a room, then
445
+ * talk to peers over `room.send` / `room.onMessage`. Distinct from
446
+ * `sdk.multiplayer`, which is the platform-arbitrated turn-based flow.
447
+ * The transport underneath is the platform's business; games see rooms,
448
+ * peers, and messages, and nothing else.
449
+ */
450
+ private realtimeController;
451
+ realtime: RealtimeNamespace;
269
452
  private emit;
270
453
  private handleMessage;
271
454
  private sendMessage;
@@ -275,4 +458,4 @@ declare class RemixSDK {
275
458
  }
276
459
  declare const sdk: RemixSDK;
277
460
 
278
- export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, type LevelProgressState, type LevelStars, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, sdk };
461
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, type LevelProgressState, type LevelStars, type MultiplayerCreateRoomEvent, type MultiplayerErrorEvent, type MultiplayerGameOverEvent, type MultiplayerIceServersEvent, type MultiplayerJoinRoomEvent, type MultiplayerLeaveRoomEvent, type MultiplayerPeersEvent, type MultiplayerRefreshIceEvent, type MultiplayerRequestInviteEvent, type MultiplayerSaveGameStateEvent, type MultiplayerSessionEndedEvent, type MultiplayerSessionEvent, type MultiplayerSignalEvent, type MultiplayerSignalsEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RealtimeEndReason, type RealtimeErrorCode, type RealtimeNamespace, type RealtimePeer, type RealtimeRoom, RealtimeRoomError, type RealtimeSendOptions, type RealtimeSignalKind, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, sdk };