@usions/sdk 2.18.0 → 2.20.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/package.json +1 -1
- package/src/browser.js +94 -1
- package/src/modules/core.js +21 -0
- package/src/modules/game-core.js +6 -0
- package/src/modules/game-methods.js +66 -0
- package/types/index.d.ts +38 -0
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -69,7 +69,7 @@ var Usion = (function () {
|
|
|
69
69
|
* Core Usion object with init, _post, _request
|
|
70
70
|
*/
|
|
71
71
|
const core = {
|
|
72
|
-
version: '2.
|
|
72
|
+
version: '2.20.0', // injected from package.json at build
|
|
73
73
|
config: {},
|
|
74
74
|
_initialized: false,
|
|
75
75
|
_initCallback: null,
|
|
@@ -190,6 +190,27 @@ var Usion = (function () {
|
|
|
190
190
|
const h = self._backendHandlers[data.event];
|
|
191
191
|
if (h) h(data.data);
|
|
192
192
|
}
|
|
193
|
+
|
|
194
|
+
// Handle a room assigned by the host AFTER launch — the solo→host
|
|
195
|
+
// promotion path. The user opened the game solo from Explore (launch
|
|
196
|
+
// mode 'single'), then shared it; the host created a room and tells us
|
|
197
|
+
// to adopt it so invitees connect to the SAME room and the host starts
|
|
198
|
+
// receiving onPlayerJoined. No-op when there is no game module.
|
|
199
|
+
if (data.type === 'GAME_ROOM_ASSIGNED' && data.roomId && self.game) {
|
|
200
|
+
var _assignedRoom = data.roomId;
|
|
201
|
+
// Reflect the new room in launch config so getLaunchParams().roomId,
|
|
202
|
+
// .mode and game.isMultiplayer() all report multiplayer from here on.
|
|
203
|
+
self.config.roomId = _assignedRoom;
|
|
204
|
+
self.config.mode = 'multiplayer';
|
|
205
|
+
// Let a solo game flip to its multiplayer UI before peers arrive.
|
|
206
|
+
self.game._dispatch('roomAssigned', { roomId: _assignedRoom });
|
|
207
|
+
// Connect + join so the host is actually in the room. Idempotent when
|
|
208
|
+
// already connected/joined; non-fatal on failure (the game can retry).
|
|
209
|
+
Promise.resolve()
|
|
210
|
+
.then(function() { return self.game.connect(); })
|
|
211
|
+
.then(function() { return self.game.join(_assignedRoom); })
|
|
212
|
+
.catch(function() { /* non-fatal */ });
|
|
213
|
+
}
|
|
193
214
|
});
|
|
194
215
|
|
|
195
216
|
// Report the user's first real interaction to the host (see below).
|
|
@@ -2357,6 +2378,72 @@ var Usion = (function () {
|
|
|
2357
2378
|
});
|
|
2358
2379
|
};
|
|
2359
2380
|
|
|
2381
|
+
/**
|
|
2382
|
+
* Invite friends to play in your room — like asking for money, but for players.
|
|
2383
|
+
*
|
|
2384
|
+
* Opens the host's friend/group picker (recent chats + search any user by
|
|
2385
|
+
* username + your groups, multi-select). Everyone you pick gets a game-invite
|
|
2386
|
+
* card in their chat; anyone who taps it joins THIS room and your game
|
|
2387
|
+
* receives `onPlayerJoined`.
|
|
2388
|
+
*
|
|
2389
|
+
* Works for any multiplayer-capable game. If the game was opened solo (from
|
|
2390
|
+
* Explore / the Game hub) with no room yet, the host creates one with you as
|
|
2391
|
+
* host and this method joins you to it — so register `onPlayerJoined` before
|
|
2392
|
+
* or right after calling invite(). Capacity is bounded by the game's
|
|
2393
|
+
* `max_players`; the picker caps selection at the remaining seats.
|
|
2394
|
+
*
|
|
2395
|
+
* Embedded feature (the host renders the picker). Standalone resolves
|
|
2396
|
+
* `{ success: false }`. Never rejects.
|
|
2397
|
+
*
|
|
2398
|
+
* @param {{ maxPlayers?: number, timeout?: number }} [opts]
|
|
2399
|
+
* @returns {Promise<{ success: boolean, roomId: string|null, invited: string[] }>}
|
|
2400
|
+
*/
|
|
2401
|
+
game.invite = function(opts) {
|
|
2402
|
+
const self = this;
|
|
2403
|
+
opts = opts || {};
|
|
2404
|
+
|
|
2405
|
+
const embedded = typeof window !== 'undefined' &&
|
|
2406
|
+
(!!window.ReactNativeWebView ||
|
|
2407
|
+
(!!window.parent && window.parent !== window));
|
|
2408
|
+
|
|
2409
|
+
const currentRoom = self.roomId || (Usion.config && Usion.config.roomId) || null;
|
|
2410
|
+
|
|
2411
|
+
if (!embedded) {
|
|
2412
|
+
try { console.warn('[Usion] game.invite needs the Usion app; resolving as not sent.'); } catch (e) { /* noop */ }
|
|
2413
|
+
return Promise.resolve({ success: false, roomId: currentRoom, invited: [] });
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
const reqData = { roomId: currentRoom };
|
|
2417
|
+
if (typeof opts.maxPlayers === 'number' && opts.maxPlayers > 0) {
|
|
2418
|
+
reqData.maxPlayers = opts.maxPlayers;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
// Long timeout — the user may sit in the picker. Never reject: a dropped
|
|
2422
|
+
// result must not throw inside the game (mirrors permissions.request).
|
|
2423
|
+
return Usion._request('GAME_INVITE_REQUEST', reqData, opts.timeout || 120000)
|
|
2424
|
+
.then(function(res) {
|
|
2425
|
+
res = res || {};
|
|
2426
|
+
const roomId = res.roomId || currentRoom || null;
|
|
2427
|
+
const result = {
|
|
2428
|
+
success: !!res.success,
|
|
2429
|
+
roomId: roomId,
|
|
2430
|
+
invited: Array.isArray(res.invited) ? res.invited : [],
|
|
2431
|
+
};
|
|
2432
|
+
if (!result.success || !roomId) return result;
|
|
2433
|
+
// Ensure the inviter is connected + joined to the room (as host) so
|
|
2434
|
+
// they get onPlayerJoined as invitees arrive. Idempotent when the game
|
|
2435
|
+
// is already in this room; invites were sent by the host regardless.
|
|
2436
|
+
return Promise.resolve()
|
|
2437
|
+
.then(function() { return self.connect(); })
|
|
2438
|
+
.then(function() { return self.join(roomId); })
|
|
2439
|
+
.then(function() { return result; })
|
|
2440
|
+
.catch(function() { return result; });
|
|
2441
|
+
})
|
|
2442
|
+
.catch(function() {
|
|
2443
|
+
return { success: false, roomId: currentRoom, invited: [] };
|
|
2444
|
+
});
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2360
2447
|
/**
|
|
2361
2448
|
* Disconnect from the game socket
|
|
2362
2449
|
*/
|
|
@@ -4549,6 +4636,7 @@ var Usion = (function () {
|
|
|
4549
4636
|
disconnect: 'disconnect',
|
|
4550
4637
|
reconnect: 'reconnect',
|
|
4551
4638
|
connection_error: 'connectionError',
|
|
4639
|
+
room_assigned: 'roomAssigned',
|
|
4552
4640
|
};
|
|
4553
4641
|
|
|
4554
4642
|
function _normalizeEventName(event) {
|
|
@@ -4694,6 +4782,11 @@ var Usion = (function () {
|
|
|
4694
4782
|
onReconnect: function(callback) { return this._setHandler('reconnect', callback); },
|
|
4695
4783
|
onConnectionError: function(callback) { return this._setHandler('connectionError', callback); },
|
|
4696
4784
|
onPlayerConnection: function(callback) { return this._setHandler('playerConnection', callback); },
|
|
4785
|
+
// Fired when the host assigns this app a room AFTER launch (the user opened
|
|
4786
|
+
// the game solo, then shared it — see GAME_ROOM_ASSIGNED). The SDK has
|
|
4787
|
+
// already updated config.roomId and is connecting+joining; use this to flip
|
|
4788
|
+
// a solo game into its multiplayer/hosting UI. onJoined fires right after.
|
|
4789
|
+
onRoomAssigned: function(callback) { return this._setHandler('roomAssigned', callback); },
|
|
4697
4790
|
|
|
4698
4791
|
/** @private Set the single legacy handler; returns an unsubscribe fn. */
|
|
4699
4792
|
_setHandler: function(name, callback) {
|
package/src/modules/core.js
CHANGED
|
@@ -187,6 +187,27 @@ export const core = {
|
|
|
187
187
|
const h = self._backendHandlers[data.event];
|
|
188
188
|
if (h) h(data.data);
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
// Handle a room assigned by the host AFTER launch — the solo→host
|
|
192
|
+
// promotion path. The user opened the game solo from Explore (launch
|
|
193
|
+
// mode 'single'), then shared it; the host created a room and tells us
|
|
194
|
+
// to adopt it so invitees connect to the SAME room and the host starts
|
|
195
|
+
// receiving onPlayerJoined. No-op when there is no game module.
|
|
196
|
+
if (data.type === 'GAME_ROOM_ASSIGNED' && data.roomId && self.game) {
|
|
197
|
+
var _assignedRoom = data.roomId;
|
|
198
|
+
// Reflect the new room in launch config so getLaunchParams().roomId,
|
|
199
|
+
// .mode and game.isMultiplayer() all report multiplayer from here on.
|
|
200
|
+
self.config.roomId = _assignedRoom;
|
|
201
|
+
self.config.mode = 'multiplayer';
|
|
202
|
+
// Let a solo game flip to its multiplayer UI before peers arrive.
|
|
203
|
+
self.game._dispatch('roomAssigned', { roomId: _assignedRoom });
|
|
204
|
+
// Connect + join so the host is actually in the room. Idempotent when
|
|
205
|
+
// already connected/joined; non-fatal on failure (the game can retry).
|
|
206
|
+
Promise.resolve()
|
|
207
|
+
.then(function() { return self.game.connect(); })
|
|
208
|
+
.then(function() { return self.game.join(_assignedRoom); })
|
|
209
|
+
.catch(function() { /* non-fatal */ });
|
|
210
|
+
}
|
|
190
211
|
});
|
|
191
212
|
|
|
192
213
|
// Report the user's first real interaction to the host (see below).
|
package/src/modules/game-core.js
CHANGED
|
@@ -27,6 +27,7 @@ const _EVENT_ALIASES = {
|
|
|
27
27
|
disconnect: 'disconnect',
|
|
28
28
|
reconnect: 'reconnect',
|
|
29
29
|
connection_error: 'connectionError',
|
|
30
|
+
room_assigned: 'roomAssigned',
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
function _normalizeEventName(event) {
|
|
@@ -172,6 +173,11 @@ export function createGameModule(Usion) {
|
|
|
172
173
|
onReconnect: function(callback) { return this._setHandler('reconnect', callback); },
|
|
173
174
|
onConnectionError: function(callback) { return this._setHandler('connectionError', callback); },
|
|
174
175
|
onPlayerConnection: function(callback) { return this._setHandler('playerConnection', callback); },
|
|
176
|
+
// Fired when the host assigns this app a room AFTER launch (the user opened
|
|
177
|
+
// the game solo, then shared it — see GAME_ROOM_ASSIGNED). The SDK has
|
|
178
|
+
// already updated config.roomId and is connecting+joining; use this to flip
|
|
179
|
+
// a solo game into its multiplayer/hosting UI. onJoined fires right after.
|
|
180
|
+
onRoomAssigned: function(callback) { return this._setHandler('roomAssigned', callback); },
|
|
175
181
|
|
|
176
182
|
/** @private Set the single legacy handler; returns an unsubscribe fn. */
|
|
177
183
|
_setHandler: function(name, callback) {
|
|
@@ -406,6 +406,72 @@ export function applyGameMethods(game, Usion) {
|
|
|
406
406
|
});
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Invite friends to play in your room — like asking for money, but for players.
|
|
411
|
+
*
|
|
412
|
+
* Opens the host's friend/group picker (recent chats + search any user by
|
|
413
|
+
* username + your groups, multi-select). Everyone you pick gets a game-invite
|
|
414
|
+
* card in their chat; anyone who taps it joins THIS room and your game
|
|
415
|
+
* receives `onPlayerJoined`.
|
|
416
|
+
*
|
|
417
|
+
* Works for any multiplayer-capable game. If the game was opened solo (from
|
|
418
|
+
* Explore / the Game hub) with no room yet, the host creates one with you as
|
|
419
|
+
* host and this method joins you to it — so register `onPlayerJoined` before
|
|
420
|
+
* or right after calling invite(). Capacity is bounded by the game's
|
|
421
|
+
* `max_players`; the picker caps selection at the remaining seats.
|
|
422
|
+
*
|
|
423
|
+
* Embedded feature (the host renders the picker). Standalone resolves
|
|
424
|
+
* `{ success: false }`. Never rejects.
|
|
425
|
+
*
|
|
426
|
+
* @param {{ maxPlayers?: number, timeout?: number }} [opts]
|
|
427
|
+
* @returns {Promise<{ success: boolean, roomId: string|null, invited: string[] }>}
|
|
428
|
+
*/
|
|
429
|
+
game.invite = function(opts) {
|
|
430
|
+
const self = this;
|
|
431
|
+
opts = opts || {};
|
|
432
|
+
|
|
433
|
+
const embedded = typeof window !== 'undefined' &&
|
|
434
|
+
(!!window.ReactNativeWebView ||
|
|
435
|
+
(!!window.parent && window.parent !== window));
|
|
436
|
+
|
|
437
|
+
const currentRoom = self.roomId || (Usion.config && Usion.config.roomId) || null;
|
|
438
|
+
|
|
439
|
+
if (!embedded) {
|
|
440
|
+
try { console.warn('[Usion] game.invite needs the Usion app; resolving as not sent.'); } catch (e) { /* noop */ }
|
|
441
|
+
return Promise.resolve({ success: false, roomId: currentRoom, invited: [] });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const reqData = { roomId: currentRoom };
|
|
445
|
+
if (typeof opts.maxPlayers === 'number' && opts.maxPlayers > 0) {
|
|
446
|
+
reqData.maxPlayers = opts.maxPlayers;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Long timeout — the user may sit in the picker. Never reject: a dropped
|
|
450
|
+
// result must not throw inside the game (mirrors permissions.request).
|
|
451
|
+
return Usion._request('GAME_INVITE_REQUEST', reqData, opts.timeout || 120000)
|
|
452
|
+
.then(function(res) {
|
|
453
|
+
res = res || {};
|
|
454
|
+
const roomId = res.roomId || currentRoom || null;
|
|
455
|
+
const result = {
|
|
456
|
+
success: !!res.success,
|
|
457
|
+
roomId: roomId,
|
|
458
|
+
invited: Array.isArray(res.invited) ? res.invited : [],
|
|
459
|
+
};
|
|
460
|
+
if (!result.success || !roomId) return result;
|
|
461
|
+
// Ensure the inviter is connected + joined to the room (as host) so
|
|
462
|
+
// they get onPlayerJoined as invitees arrive. Idempotent when the game
|
|
463
|
+
// is already in this room; invites were sent by the host regardless.
|
|
464
|
+
return Promise.resolve()
|
|
465
|
+
.then(function() { return self.connect(); })
|
|
466
|
+
.then(function() { return self.join(roomId); })
|
|
467
|
+
.then(function() { return result; })
|
|
468
|
+
.catch(function() { return result; });
|
|
469
|
+
})
|
|
470
|
+
.catch(function() {
|
|
471
|
+
return { success: false, roomId: currentRoom, invited: [] };
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
|
|
409
475
|
/**
|
|
410
476
|
* Disconnect from the game socket
|
|
411
477
|
*/
|
package/types/index.d.ts
CHANGED
|
@@ -256,6 +256,24 @@ export interface RematchData {
|
|
|
256
256
|
player_id: string;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/** Options for game.invite(). */
|
|
260
|
+
export interface GameInviteOptions {
|
|
261
|
+
/** Cap the room at this many players (otherwise the game's max_players). */
|
|
262
|
+
maxPlayers?: number;
|
|
263
|
+
/** Max ms to wait for the user to finish in the picker (default 120000). */
|
|
264
|
+
timeout?: number;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Result of game.invite(). */
|
|
268
|
+
export interface GameInviteResult {
|
|
269
|
+
/** True if the user sent at least one invite. */
|
|
270
|
+
success: boolean;
|
|
271
|
+
/** The room invitees will join (you are host); null if nothing was sent. */
|
|
272
|
+
roomId: string | null;
|
|
273
|
+
/** User/group ids the invite was sent to. */
|
|
274
|
+
invited: string[];
|
|
275
|
+
}
|
|
276
|
+
|
|
259
277
|
export interface GameModule {
|
|
260
278
|
// Connection
|
|
261
279
|
connect(socketUrl?: string, token?: string): Promise<void>;
|
|
@@ -292,6 +310,17 @@ export interface GameModule {
|
|
|
292
310
|
requestRematch(): void;
|
|
293
311
|
forfeit(): Promise<{ success: boolean; error?: string }>;
|
|
294
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Invite friends to play in your room. Opens the host's friend/group picker
|
|
315
|
+
* (recent chats + username search + your groups, multi-select); each pick gets
|
|
316
|
+
* a game-invite card in their chat, and anyone who taps it joins THIS room
|
|
317
|
+
* (your game gets `onPlayerJoined`). Works for any multiplayer-capable game —
|
|
318
|
+
* if launched solo with no room, the host creates one with you as host and
|
|
319
|
+
* this joins you to it. Embedded only; standalone resolves `{success:false}`.
|
|
320
|
+
* Never rejects. (SDK ≥ 2.19)
|
|
321
|
+
*/
|
|
322
|
+
invite(opts?: GameInviteOptions): Promise<GameInviteResult>;
|
|
323
|
+
|
|
295
324
|
/**
|
|
296
325
|
* Checkpoint authoritative game state on the server (authority only —
|
|
297
326
|
* player_ids[0]/host). (Re)joining clients receive the latest checkpoint
|
|
@@ -326,6 +355,15 @@ export interface GameModule {
|
|
|
326
355
|
onConnectionError(callback: (error: Error) => void): UnsubscribeFn;
|
|
327
356
|
/** Peer connection lifecycle: connected / reconnecting (grace) / gone. */
|
|
328
357
|
onPlayerConnection(callback: (data: PlayerConnectionData) => void): UnsubscribeFn;
|
|
358
|
+
/**
|
|
359
|
+
* Fired when the host assigns this app a room AFTER launch — the solo→host
|
|
360
|
+
* promotion path. The user opened the game solo (launch mode 'single'), then
|
|
361
|
+
* shared it from the host's top-bar Share button; the host created a room and
|
|
362
|
+
* the SDK has already updated `getLaunchParams().roomId`/`.mode` and is
|
|
363
|
+
* connecting+joining. Use this to flip a solo game into its multiplayer /
|
|
364
|
+
* hosting UI; `onJoined` fires right after the join completes.
|
|
365
|
+
*/
|
|
366
|
+
onRoomAssigned(callback: (data: { roomId: string }) => void): UnsubscribeFn;
|
|
329
367
|
|
|
330
368
|
/**
|
|
331
369
|
* Register an ADDITIONAL event listener. Unlike the onX methods this
|