@indiegems/gem-web-sdk 10.4.0 → 11.1.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.
@@ -2,7 +2,7 @@
2
2
  "schema_version": 2,
3
3
  "source": {
4
4
  "repo": "IndieGems/gem-web-sdk",
5
- "version": "10.4.0"
5
+ "version": "11.1.0"
6
6
  },
7
7
  "entries": [
8
8
  {
@@ -6632,11 +6632,18 @@
6632
6632
  "name": "requestAvatarPicker",
6633
6633
  "anchor": "gemframe-requestavatarpicker",
6634
6634
  "group": "frame",
6635
- "signature": "requestAvatarPicker(): boolean",
6635
+ "signature": "requestAvatarPicker(tab?: \"2d\" | \"3d\"): boolean",
6636
6636
  "documented": true,
6637
- "summary": "Ask the arcade to open its own avatar picker over your game. Nothing comes\nback: a changed portrait arrives as a new `gem.player`, through `onPlayer`.\n\nPrefer `gem.openAvatarPicker`, which documents what a game does with the\nchange.",
6637
+ "summary": "Ask the arcade to open its own avatar picker over your game. Nothing comes\nback: a changed portrait or 3D character arrives as a new `gem.player`,\nthrough `onPlayer`.\n\nPrefer `gem.openAvatarPicker`, which documents what a game does with the\nchange.",
6638
6638
  "deprecated": null,
6639
- "params": [],
6639
+ "params": [
6640
+ {
6641
+ "name": "tab",
6642
+ "type": "\"2d\" | \"3d\"",
6643
+ "optional": true,
6644
+ "summary": "Which tab to open on — `'2d'` (the portrait) or `'3d'` (the 3D\n character). Omit it to let the arcade decide. Anything else is treated as\n omitted. An arcade older than the tabs ignores it."
6645
+ }
6646
+ ],
6640
6647
  "returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade that has no avatar picker here, and past this frame's\n own limit of six a minute.",
6641
6648
  "throws": null,
6642
6649
  "example": null,
@@ -6824,6 +6831,54 @@
6824
6831
  "defaultValue": null,
6825
6832
  "guides": []
6826
6833
  },
6834
+ {
6835
+ "id": "Friend.avatar3dId",
6836
+ "name": "avatar3dId",
6837
+ "anchor": "friend-avatar3did",
6838
+ "group": "friends",
6839
+ "signature": "readonly avatar3dId?: string",
6840
+ "documented": true,
6841
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
6842
+ "deprecated": null,
6843
+ "params": [],
6844
+ "returns": null,
6845
+ "throws": null,
6846
+ "example": null,
6847
+ "defaultValue": null,
6848
+ "guides": []
6849
+ },
6850
+ {
6851
+ "id": "Friend.avatar3dModelUrl",
6852
+ "name": "avatar3dModelUrl",
6853
+ "anchor": "friend-avatar3dmodelurl",
6854
+ "group": "friends",
6855
+ "signature": "readonly avatar3dModelUrl?: string",
6856
+ "documented": true,
6857
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
6858
+ "deprecated": null,
6859
+ "params": [],
6860
+ "returns": null,
6861
+ "throws": null,
6862
+ "example": null,
6863
+ "defaultValue": null,
6864
+ "guides": []
6865
+ },
6866
+ {
6867
+ "id": "Friend.avatar3dIconUrl",
6868
+ "name": "avatar3dIconUrl",
6869
+ "anchor": "friend-avatar3diconurl",
6870
+ "group": "friends",
6871
+ "signature": "readonly avatar3dIconUrl?: string",
6872
+ "documented": true,
6873
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
6874
+ "deprecated": null,
6875
+ "params": [],
6876
+ "returns": null,
6877
+ "throws": null,
6878
+ "example": null,
6879
+ "defaultValue": null,
6880
+ "guides": []
6881
+ },
6827
6882
  {
6828
6883
  "id": "Friend.badges",
6829
6884
  "name": "badges",
@@ -8594,6 +8649,54 @@
8594
8649
  "defaultValue": null,
8595
8650
  "guides": []
8596
8651
  },
8652
+ {
8653
+ "id": "GemPlayer.avatar3dId",
8654
+ "name": "avatar3dId",
8655
+ "anchor": "gemplayer-avatar3did",
8656
+ "group": "player",
8657
+ "signature": "readonly avatar3dId?: string",
8658
+ "documented": true,
8659
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
8660
+ "deprecated": null,
8661
+ "params": [],
8662
+ "returns": null,
8663
+ "throws": null,
8664
+ "example": null,
8665
+ "defaultValue": null,
8666
+ "guides": []
8667
+ },
8668
+ {
8669
+ "id": "GemPlayer.avatar3dModelUrl",
8670
+ "name": "avatar3dModelUrl",
8671
+ "anchor": "gemplayer-avatar3dmodelurl",
8672
+ "group": "player",
8673
+ "signature": "readonly avatar3dModelUrl?: string",
8674
+ "documented": true,
8675
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
8676
+ "deprecated": null,
8677
+ "params": [],
8678
+ "returns": null,
8679
+ "throws": null,
8680
+ "example": null,
8681
+ "defaultValue": null,
8682
+ "guides": []
8683
+ },
8684
+ {
8685
+ "id": "GemPlayer.avatar3dIconUrl",
8686
+ "name": "avatar3dIconUrl",
8687
+ "anchor": "gemplayer-avatar3diconurl",
8688
+ "group": "player",
8689
+ "signature": "readonly avatar3dIconUrl?: string",
8690
+ "documented": true,
8691
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
8692
+ "deprecated": null,
8693
+ "params": [],
8694
+ "returns": null,
8695
+ "throws": null,
8696
+ "example": null,
8697
+ "defaultValue": null,
8698
+ "guides": []
8699
+ },
8597
8700
  {
8598
8701
  "id": "GemPlayer.badges",
8599
8702
  "name": "badges",
@@ -9756,11 +9859,18 @@
9756
9859
  "name": "openAvatarPicker",
9757
9860
  "anchor": "gem-openavatarpicker",
9758
9861
  "group": "start",
9759
- "signature": "openAvatarPicker(): boolean",
9862
+ "signature": "openAvatarPicker(options?: { tab?: \"2d\" | \"3d\" }): boolean",
9760
9863
  "documented": true,
9761
- "summary": "Open the arcade's avatar picker over your game, so the player can change\nthe Arcade avatar they wear without leaving it. For a game that draws\nArcade avatars — whole characters the player owns as Arcade items — put a\n\"Change avatar\" button in your own UI and call this from it:\n\n```js\nchangeAvatar.hidden = !gem.avatarPickerAvailable;\nchangeAvatar.onclick = () => {\n if (!gem.openAvatarPicker()) changeAvatar.hidden = true;\n};\n```\n\n**The change arrives as a new player, not as an answer.** Nothing comes\nback from this call. When the player picks a different avatar, the arcade\npauses your game while the picker is open, then sends the player again —\nyour `onPlayer` callback runs and [Gem.player](api-reference-game.md#gem-player) holds the new\n[PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) — and resumes it. Redraw from\n`onPlayer`, which also runs when the player changes their avatar from the\narcade's own menu without your game asking. A player who closes the picker\nunchanged produces nothing at all.\n\n**Your server learns it only when it reads the room again.** A\nserver-authoritative game reads each member's `avatar_icon_id` from the\nroom read (`GET /v1/games/{game_id}/game-rooms/{game_room_id}`, or\n`gem.room.get()` on a dedicated server); nothing tells the server the\nplayer changed. So when `onPlayer` brings a different `avatarIconId`, have\nyour client tell your server, and have the server re-read the room — never\ntrust the id the client sends.",
9864
+ "summary": "Open the arcade's avatar picker over your game, so the player can change\nthe Arcade avatar they wear without leaving it. For a game that draws\nArcade avatars — whole characters the player owns as Arcade items — put a\n\"Change avatar\" button in your own UI and call this from it:\n\n```js\nchangeAvatar.hidden = !gem.avatarPickerAvailable;\nchangeAvatar.onclick = () => {\n if (!gem.openAvatarPicker()) changeAvatar.hidden = true;\n};\n```\n\n**Two tabs: 2D and 3D.** The picker has a 2D tab — the portrait, a\npicture ([PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid)) — and a 3D tab — a whole\ncharacter with its own model ([PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did),\n[PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl)). They are separate choices: wearing\none never changes the other. Pass `{ tab: '3d' }` or `{ tab: '2d' }` to\nopen on one; omit it and the arcade decides (a game that draws Arcade\navatars gets the 3D tab). An arcade older than the tabs ignores it.\n\n**The change arrives as a new player, not as an answer.** Nothing comes\nback from this call. When the player picks a different avatar, the arcade\npauses your game while the picker is open, then sends the player again —\nyour `onPlayer` callback runs and [Gem.player](api-reference-game.md#gem-player) holds the new\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) or [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) —\nand resumes it. Redraw from `onPlayer`, which also runs when the player\nchanges their avatar from the arcade's own menu without your game asking.\nA player who closes the picker unchanged produces nothing at all.\n\n**Your server learns it only when it reads the room again.** A\nserver-authoritative game reads each member's `avatar_3d_id` and\n`avatar_icon_id` from the room read\n(`GET /v1/games/{game_id}/game-rooms/{game_room_id}`, or `gem.room.get()`\non a dedicated server); nothing tells the server the player changed. So\nwhen `onPlayer` brings a different avatar, have your client tell your\nserver, and have the server re-read the room — never trust the id the\nclient sends.",
9762
9865
  "deprecated": null,
9763
- "params": [],
9866
+ "params": [
9867
+ {
9868
+ "name": "options",
9869
+ "type": "{ tab?: \"2d\" | \"3d\" }",
9870
+ "optional": true,
9871
+ "summary": "`tab` — `'2d'` or `'3d'`, the tab to open on. Omit it to let\n the arcade decide; any other value is treated as omitted."
9872
+ }
9873
+ ],
9764
9874
  "returns": "`true` when the arcade was asked; `false` when it was not and\n never will be for this call — outside the arcade, before the bridge is\n up, under an embedder with no avatar picker, or past six asks a minute.\n It never throws and never waits.",
9765
9875
  "throws": null,
9766
9876
  "example": null,
@@ -11419,6 +11529,54 @@
11419
11529
  "defaultValue": null,
11420
11530
  "guides": []
11421
11531
  },
11532
+ {
11533
+ "id": "PlayerIdentity.avatar3dId",
11534
+ "name": "avatar3dId",
11535
+ "anchor": "playeridentity-avatar3did",
11536
+ "group": "player",
11537
+ "signature": "readonly avatar3dId?: string",
11538
+ "documented": true,
11539
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
11540
+ "deprecated": null,
11541
+ "params": [],
11542
+ "returns": null,
11543
+ "throws": null,
11544
+ "example": null,
11545
+ "defaultValue": null,
11546
+ "guides": []
11547
+ },
11548
+ {
11549
+ "id": "PlayerIdentity.avatar3dModelUrl",
11550
+ "name": "avatar3dModelUrl",
11551
+ "anchor": "playeridentity-avatar3dmodelurl",
11552
+ "group": "player",
11553
+ "signature": "readonly avatar3dModelUrl?: string",
11554
+ "documented": true,
11555
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
11556
+ "deprecated": null,
11557
+ "params": [],
11558
+ "returns": null,
11559
+ "throws": null,
11560
+ "example": null,
11561
+ "defaultValue": null,
11562
+ "guides": []
11563
+ },
11564
+ {
11565
+ "id": "PlayerIdentity.avatar3dIconUrl",
11566
+ "name": "avatar3dIconUrl",
11567
+ "anchor": "playeridentity-avatar3diconurl",
11568
+ "group": "player",
11569
+ "signature": "readonly avatar3dIconUrl?: string",
11570
+ "documented": true,
11571
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
11572
+ "deprecated": null,
11573
+ "params": [],
11574
+ "returns": null,
11575
+ "throws": null,
11576
+ "example": null,
11577
+ "defaultValue": null,
11578
+ "guides": []
11579
+ },
11422
11580
  {
11423
11581
  "id": "PlayerIdentity.badges",
11424
11582
  "name": "badges",
@@ -11442,7 +11600,7 @@
11442
11600
  "group": "player",
11443
11601
  "signature": "function mapIdentity(row: unknown): PlayerIdentity",
11444
11602
  "documented": true,
11445
- "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-game.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url` and `badges`, and nothing else from the\nrow. Anything missing, empty or of the wrong type is absent in the result.",
11603
+ "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-game.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,\n`avatar_3d_icon_url` and `badges`, and nothing else from the row. Anything missing, empty or of the wrong type is absent in the result.",
11446
11604
  "deprecated": null,
11447
11605
  "params": [
11448
11606
  {
@@ -11465,7 +11623,7 @@
11465
11623
  "group": "player",
11466
11624
  "signature": "function platformIconUrl(url: string | undefined): string | undefined",
11467
11625
  "documented": true,
11468
- "summary": "The icon URL, if and only if the platform serves it — otherwise `undefined`.\n\nUse it on every [PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl) and\n[PlayerBadge.iconUrl](api-reference-game.md#playerbadge-iconurl) before it reaches an `<img src>`, a texture\nloader or a `fetch`:\n\n```js\nimport { platformIconUrl } from '@indiegems/gem-web-sdk/sdk';\n\nconst src = platformIconUrl(member.avatarIconUrl);\nif (src) img.src = src; else drawPlaceholder();\n```\n\n**Why it exists.** Loading an image is a request, and a request carries the\npage's `Referer` and the moment it happened. The URLs the SDK hands you are\nchecked for shape — `https`, no credentials — but a shape check cannot know\nwhich host is the platform's, so an icon URL from anywhere but the platform's\nown image host would be a beacon on your player. This compares the whole\norigin against the hosts the platform serves item icons from in production. A\nURL on `localhost` passes too, for a stack running on one machine.\n\nAn unrecognised host answers `undefined` — draw your placeholder, as for a\nplayer with no portrait. That includes a development backend's image host,\nso portraits appear only against production and loopback.",
11626
+ "summary": "The icon URL, if and only if the platform serves it — otherwise `undefined`.\n\nUse it on every [PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl),\n[PlayerIdentity.avatar3dIconUrl](api-reference-game.md#playeridentity-avatar3diconurl), [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl)\nand [PlayerBadge.iconUrl](api-reference-game.md#playerbadge-iconurl) before it reaches an `<img src>`, a texture\nloader, a `GLTFLoader` or a `fetch`:\n\n```js\nimport { platformIconUrl } from '@indiegems/gem-web-sdk/sdk';\n\nconst src = platformIconUrl(member.avatarIconUrl);\nif (src) img.src = src; else drawPlaceholder();\n```\n\n**Why it exists.** Loading an image is a request, and a request carries the\npage's `Referer` and the moment it happened. The URLs the SDK hands you are\nchecked for shape — `https`, no credentials — but a shape check cannot know\nwhich host is the platform's, so an icon URL from anywhere but the platform's\nown image host would be a beacon on your player. This compares the whole\norigin against the hosts the platform serves item icons from in production. A\nURL on `localhost` passes too, for a stack running on one machine.\n\nAn unrecognised host answers `undefined` — draw your placeholder, as for a\nplayer with no portrait. That includes a development backend's image host,\nso portraits appear only against production and loopback.",
11469
11627
  "deprecated": null,
11470
11628
  "params": [
11471
11629
  {
@@ -14207,6 +14365,54 @@
14207
14365
  "defaultValue": null,
14208
14366
  "guides": []
14209
14367
  },
14368
+ {
14369
+ "id": "LeaderboardEntry.avatar3dId",
14370
+ "name": "avatar3dId",
14371
+ "anchor": "leaderboardentry-avatar3did",
14372
+ "group": "leaderboards",
14373
+ "signature": "readonly avatar3dId?: string",
14374
+ "documented": true,
14375
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
14376
+ "deprecated": null,
14377
+ "params": [],
14378
+ "returns": null,
14379
+ "throws": null,
14380
+ "example": null,
14381
+ "defaultValue": null,
14382
+ "guides": []
14383
+ },
14384
+ {
14385
+ "id": "LeaderboardEntry.avatar3dModelUrl",
14386
+ "name": "avatar3dModelUrl",
14387
+ "anchor": "leaderboardentry-avatar3dmodelurl",
14388
+ "group": "leaderboards",
14389
+ "signature": "readonly avatar3dModelUrl?: string",
14390
+ "documented": true,
14391
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
14392
+ "deprecated": null,
14393
+ "params": [],
14394
+ "returns": null,
14395
+ "throws": null,
14396
+ "example": null,
14397
+ "defaultValue": null,
14398
+ "guides": []
14399
+ },
14400
+ {
14401
+ "id": "LeaderboardEntry.avatar3dIconUrl",
14402
+ "name": "avatar3dIconUrl",
14403
+ "anchor": "leaderboardentry-avatar3diconurl",
14404
+ "group": "leaderboards",
14405
+ "signature": "readonly avatar3dIconUrl?: string",
14406
+ "documented": true,
14407
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
14408
+ "deprecated": null,
14409
+ "params": [],
14410
+ "returns": null,
14411
+ "throws": null,
14412
+ "example": null,
14413
+ "defaultValue": null,
14414
+ "guides": []
14415
+ },
14210
14416
  {
14211
14417
  "id": "LeaderboardEntry.badges",
14212
14418
  "name": "badges",
@@ -15970,6 +16176,54 @@
15970
16176
  "defaultValue": null,
15971
16177
  "guides": []
15972
16178
  },
16179
+ {
16180
+ "id": "PlayerProfile.avatar3dId",
16181
+ "name": "avatar3dId",
16182
+ "anchor": "playerprofile-avatar3did",
16183
+ "group": "profiles",
16184
+ "signature": "readonly avatar3dId?: string",
16185
+ "documented": true,
16186
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
16187
+ "deprecated": null,
16188
+ "params": [],
16189
+ "returns": null,
16190
+ "throws": null,
16191
+ "example": null,
16192
+ "defaultValue": null,
16193
+ "guides": []
16194
+ },
16195
+ {
16196
+ "id": "PlayerProfile.avatar3dModelUrl",
16197
+ "name": "avatar3dModelUrl",
16198
+ "anchor": "playerprofile-avatar3dmodelurl",
16199
+ "group": "profiles",
16200
+ "signature": "readonly avatar3dModelUrl?: string",
16201
+ "documented": true,
16202
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
16203
+ "deprecated": null,
16204
+ "params": [],
16205
+ "returns": null,
16206
+ "throws": null,
16207
+ "example": null,
16208
+ "defaultValue": null,
16209
+ "guides": []
16210
+ },
16211
+ {
16212
+ "id": "PlayerProfile.avatar3dIconUrl",
16213
+ "name": "avatar3dIconUrl",
16214
+ "anchor": "playerprofile-avatar3diconurl",
16215
+ "group": "profiles",
16216
+ "signature": "readonly avatar3dIconUrl?: string",
16217
+ "documented": true,
16218
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
16219
+ "deprecated": null,
16220
+ "params": [],
16221
+ "returns": null,
16222
+ "throws": null,
16223
+ "example": null,
16224
+ "defaultValue": null,
16225
+ "guides": []
16226
+ },
15973
16227
  {
15974
16228
  "id": "PlayerProfile.badges",
15975
16229
  "name": "badges",
@@ -16387,6 +16641,22 @@
16387
16641
  "defaultValue": null,
16388
16642
  "guides": []
16389
16643
  },
16644
+ {
16645
+ "id": "QuickJoinOptions.templateSlugs",
16646
+ "name": "templateSlugs",
16647
+ "anchor": "quickjoinoptions-templateslugs",
16648
+ "group": "rooms",
16649
+ "signature": "readonly templateSlugs?: readonly string[]",
16650
+ "documented": true,
16651
+ "summary": "Several templates this player accepts, by slug — one to eight of them, for\na game that offers \"any of these modes\". Pass it INSTEAD of\n[QuickJoinOptions.templateSlug](api-reference-game.md#quickjoinoptions-templateslug), never beside it.\n\nOpen rooms of every listed template are candidates together, and the\nfullest one is joined. **The order matters only when there is none:** the\nroom that gets created is made from the first slug listed. Read\n`room.templateSlug` to learn which mode the player landed in.\n\nEvery listed template must be one players can quick-join; one that is not\nrefuses the whole call rather than being skipped.",
16652
+ "deprecated": null,
16653
+ "params": [],
16654
+ "returns": null,
16655
+ "throws": null,
16656
+ "example": "```ts\nconst room = await gem.rooms.quickJoin({ templateSlugs: ['duel', 'brawl'] });\nstartMode(room.templateSlug);\n```",
16657
+ "defaultValue": null,
16658
+ "guides": []
16659
+ },
16390
16660
  {
16391
16661
  "id": "QuickJoinOptions.templateId",
16392
16662
  "name": "templateId",
@@ -17270,7 +17540,7 @@
17270
17540
  }
17271
17541
  ],
17272
17542
  "returns": "The ticket, still `queued` unless a lobby was waiting for exactly\nthis player.",
17273
- "throws": "`GemApiError` when the platform refuses the ticket. Two refusals\nare worth their own branch, by [GemApiError.code](api-reference-game.md#gemapierror-code):\n`already_queued` — this player, or someone in their party, is already\non a ticket, possibly in another tab or another game — and\n`matchmaking_unavailable`, which means matchmaking is turned off or not\nanswering. Tell the player; do not loop on it, and do not quietly\nsubstitute `quickJoin`, which puts them in a different kind of room.",
17543
+ "throws": "`GemApiError` when the platform refuses the ticket. Two refusals\nare worth their own branch, by [GemApiError.code](api-reference-game.md#gemapierror-code):\n`already_queued` — this player, or someone in their party, is already\non a ticket, possibly in another tab or another game — and\n`matchmaking_unavailable`, which means matchmaking is turned off or not\nanswering. Tell the player; do not loop on it, and do not quietly\nsubstitute `quickJoin`, which puts them in a different kind of room.\n\nA party leader can meet three more, when the party could not be seated\ntogether: `version_data_required` — the party did not launch this game\ntogether; `party_versions_incomplete` — a member's game has not finished\nstarting, so wait a moment and queue again; and `party_version_mismatch`\n— a member is on a build that cannot play with the leader's, so everyone\nreloads the game. No ticket was made in any of the three.",
17274
17544
  "example": null,
17275
17545
  "defaultValue": null,
17276
17546
  "guides": []
@@ -18786,6 +19056,54 @@
18786
19056
  "defaultValue": null,
18787
19057
  "guides": []
18788
19058
  },
19059
+ {
19060
+ "id": "RoomMember.avatar3dId",
19061
+ "name": "avatar3dId",
19062
+ "anchor": "roommember-avatar3did",
19063
+ "group": "rooms",
19064
+ "signature": "readonly avatar3dId?: string",
19065
+ "documented": true,
19066
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
19067
+ "deprecated": null,
19068
+ "params": [],
19069
+ "returns": null,
19070
+ "throws": null,
19071
+ "example": null,
19072
+ "defaultValue": null,
19073
+ "guides": []
19074
+ },
19075
+ {
19076
+ "id": "RoomMember.avatar3dModelUrl",
19077
+ "name": "avatar3dModelUrl",
19078
+ "anchor": "roommember-avatar3dmodelurl",
19079
+ "group": "rooms",
19080
+ "signature": "readonly avatar3dModelUrl?: string",
19081
+ "documented": true,
19082
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
19083
+ "deprecated": null,
19084
+ "params": [],
19085
+ "returns": null,
19086
+ "throws": null,
19087
+ "example": null,
19088
+ "defaultValue": null,
19089
+ "guides": []
19090
+ },
19091
+ {
19092
+ "id": "RoomMember.avatar3dIconUrl",
19093
+ "name": "avatar3dIconUrl",
19094
+ "anchor": "roommember-avatar3diconurl",
19095
+ "group": "rooms",
19096
+ "signature": "readonly avatar3dIconUrl?: string",
19097
+ "documented": true,
19098
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
19099
+ "deprecated": null,
19100
+ "params": [],
19101
+ "returns": null,
19102
+ "throws": null,
19103
+ "example": null,
19104
+ "defaultValue": null,
19105
+ "guides": []
19106
+ },
18789
19107
  {
18790
19108
  "id": "RoomMember.badges",
18791
19109
  "name": "badges",
@@ -19244,6 +19562,22 @@
19244
19562
  "defaultValue": null,
19245
19563
  "guides": []
19246
19564
  },
19565
+ {
19566
+ "id": "Room.templateSlug",
19567
+ "name": "templateSlug",
19568
+ "anchor": "room-templateslug",
19569
+ "group": "rooms",
19570
+ "signature": "readonly templateSlug?: string",
19571
+ "documented": true,
19572
+ "summary": "The slug of the template this room was made from — the same slug you pass\nas `templateSlug`. After a `quickJoin` naming several templates, this is\nhow a game learns which mode the player landed in.\n\nAbsent for a room made without a template.",
19573
+ "deprecated": null,
19574
+ "params": [],
19575
+ "returns": null,
19576
+ "throws": null,
19577
+ "example": null,
19578
+ "defaultValue": null,
19579
+ "guides": []
19580
+ },
19247
19581
  {
19248
19582
  "id": "Room.roomType",
19249
19583
  "name": "roomType",
@@ -19868,6 +20202,54 @@
19868
20202
  "defaultValue": null,
19869
20203
  "guides": []
19870
20204
  },
20205
+ {
20206
+ "id": "ShowcaseFriendEntry.avatar3dId",
20207
+ "name": "avatar3dId",
20208
+ "anchor": "showcasefriendentry-avatar3did",
20209
+ "group": "showcase",
20210
+ "signature": "readonly avatar3dId?: string",
20211
+ "documented": true,
20212
+ "summary": "The id of the 3D character the player wears, when they wear one — an\nArcade item of type `avatar_3d`, chosen on the 3D tab of the Arcade's\navatar picker. What a game that draws Arcade avatars keys its character\non: prefer it, load [PlayerIdentity.avatar3dModelUrl](api-reference-game.md#playeridentity-avatar3dmodelurl), and fall back\nto your own mapping of [PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) when it is\nabsent.\n\nIndependent of the portrait: wearing a 3D character never changes\n`avatarIconId`, and the other way round. Absent means the game's default\nlook. On `gem.player` it changes when the player wears another one, and\n`onPlayer` runs again; see `gem.openAvatarPicker`.",
20213
+ "deprecated": null,
20214
+ "params": [],
20215
+ "returns": null,
20216
+ "throws": null,
20217
+ "example": null,
20218
+ "defaultValue": null,
20219
+ "guides": []
20220
+ },
20221
+ {
20222
+ "id": "ShowcaseFriendEntry.avatar3dModelUrl",
20223
+ "name": "avatar3dModelUrl",
20224
+ "anchor": "showcasefriendentry-avatar3dmodelurl",
20225
+ "group": "showcase",
20226
+ "signature": "readonly avatar3dModelUrl?: string",
20227
+ "documented": true,
20228
+ "summary": "The worn 3D character's model — a binary glTF (`.glb`) with one rig,\n`root > body > (head, arm_l, arm_r > hand_socket, leg_l, leg_r)`, and\npossibly animation clips. Load it with three.js's `GLTFLoader`, after\npassing it through [platformIconUrl](api-reference-game.md#platformiconurl), which pins models to the\nplatform's item host exactly as it pins pictures. Absent when no 3D\ncharacter is worn.",
20229
+ "deprecated": null,
20230
+ "params": [],
20231
+ "returns": null,
20232
+ "throws": null,
20233
+ "example": null,
20234
+ "defaultValue": null,
20235
+ "guides": []
20236
+ },
20237
+ {
20238
+ "id": "ShowcaseFriendEntry.avatar3dIconUrl",
20239
+ "name": "avatar3dIconUrl",
20240
+ "anchor": "showcasefriendentry-avatar3diconurl",
20241
+ "group": "showcase",
20242
+ "signature": "readonly avatar3dIconUrl?: string",
20243
+ "documented": true,
20244
+ "summary": "A rendered portrait of the worn 3D character, for a roster or a menu where\nloading the model is too much. Not the 2D portrait\n([PlayerIdentity.avatarIconUrl](api-reference-game.md#playeridentity-avatariconurl)), which is a separate choice. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
20245
+ "deprecated": null,
20246
+ "params": [],
20247
+ "returns": null,
20248
+ "throws": null,
20249
+ "example": null,
20250
+ "defaultValue": null,
20251
+ "guides": []
20252
+ },
19871
20253
  {
19872
20254
  "id": "ShowcaseFriendEntry.badges",
19873
20255
  "name": "badges",
package/docs/changelog.md CHANGED
@@ -16,6 +16,32 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 11.1.0
20
+
21
+ Upgrading from 11.0.0. Impact: none.
22
+
23
+ ### New capability
24
+
25
+ - Quick join options now accept templateSlugs, a list of one to eight templates. Open rooms of every listed template are candidates together, and if a room has to be created it is made from the first template in the list. Do not pass the list together with a single template, and do not pass an empty list or more than eight, because those calls are refused locally. When a local tool hosts your game's server, only the first template is sent and the room is always created from it. Games that pass a single template keep working unchanged.
26
+ - A room now reports which template it was made from through Room.templateSlug. Use it to tell which mode a player landed in, especially when you offer several templates at once.
27
+
28
+ ## 11.0.0
29
+
30
+ Upgrading from 10.4.0. Impact: required.
31
+
32
+ ### Rebuild
33
+
34
+ - The calls that ask the Arcade to open the avatar picker now accept an optional tab, either 2d or 3d. Existing calls without arguments still compile and behave as before. Recompile; pass a tab only if you want the picker to open on a specific one.
35
+ - The host callback for avatar picker requests now receives a request object that may carry the requested tab. Handlers that take no arguments still compile. Recompile, and if you host games, read the tab to open the picker on the right one.
36
+
37
+ ### New capability
38
+
39
+ - The current player now carries optional 3D avatar fields: an identifier, a model URL and an icon URL. They describe the 3D character the player wears in the Arcade, which is chosen independently of the 2D portrait. The fields may be absent, so keep a fallback to the portrait. They are sent again whenever the worn 3D character changes, so a game that shows the player's avatar should update when they change.
40
+ - Room members, leaderboard rows, friends and showcase friend rows now carry the same optional 3D avatar identifier, model URL and icon URL. A game can show other players' 3D characters wherever it already shows their portraits. Treat the fields as optional and fall back to the portrait when they are missing.
41
+ - Code that embeds a game and sends player details to it can now pass the 3D avatar identifier, model URL and icon URL along with the other player fields. Send them whenever the player's worn 3D character changes.
42
+ - The raw server and API identity types now include optional 3D avatar identifier, model URL and icon URL properties for players and subjects. Code that reads these types directly can use them. They may not be populated by the service yet, so handle their absence.
43
+ - The Arcade avatar picker now has a 2D tab and a 3D tab. A game asking the Arcade to open the picker can say which tab to show, and a host receiving that request can read the requested tab. Omitting the tab keeps the previous behaviour. An unknown tab value is dropped rather than failing the request.
44
+
19
45
  ## 10.4.0
20
46
 
21
47
  Upgrading from 10.3.1. Impact: required.
@@ -50,7 +50,7 @@ either way.
50
50
  ```jsonc
51
51
  // package.json
52
52
  "dependencies": {
53
- "@indiegems/gem-web-sdk": "10.4.0"
53
+ "@indiegems/gem-web-sdk": "11.1.0"
54
54
  }
55
55
  ```
56
56
 
@@ -33,8 +33,8 @@
33
33
  },
34
34
  {
35
35
  "file": "changelog.md",
36
- "sha256": "fd69a4c08141e0646d695adedce0d313de2295cfd6bc656433ce33dd02ec731a",
37
- "updated": "2026-10-11T19:38:28Z"
36
+ "sha256": "0b0740920776dd0eae95fec66d21fcdb3370eaec1ddedfc960abc87e88aa4fb5",
37
+ "updated": "2026-10-12T00:35:07Z"
38
38
  },
39
39
  {
40
40
  "file": "cross-promotion.md",
@@ -68,8 +68,8 @@
68
68
  },
69
69
  {
70
70
  "file": "getting-started.md",
71
- "sha256": "f2c13478823888b777058bbdc4de41216dd8b48d7e7efab3a31334f980d647ec",
72
- "updated": "2026-10-11T19:38:28Z"
71
+ "sha256": "550015876f24c9e928e40ddc4faf0531ccb9a58697b0012156d86509f67e55b4",
72
+ "updated": "2026-10-12T00:35:07Z"
73
73
  },
74
74
  {
75
75
  "file": "inventory-and-catalogs.md",
@@ -113,8 +113,8 @@
113
113
  },
114
114
  {
115
115
  "file": "rooms-and-matches.md",
116
- "sha256": "be22367f8dc9147e815a14ddb417788fa6614720a0df4f9f01624bf4667877d3",
117
- "updated": "2026-10-11T10:17:11Z"
116
+ "sha256": "e5cd7655713aba7d3b978cfa579bbbb14cb85f271668002e1c41975f5967a2c8",
117
+ "updated": "2026-10-12T00:19:34Z"
118
118
  },
119
119
  {
120
120
  "file": "saving-player-progress.md",
@@ -123,8 +123,8 @@
123
123
  },
124
124
  {
125
125
  "file": "server-bundles.md",
126
- "sha256": "04bb5fb5f631a52c7962728a74602ce7eec6ee6a1a3efa6197542ce5aa4cce95",
127
- "updated": "2026-10-11T19:38:28Z"
126
+ "sha256": "cfa1bcfcd8f46c4208a009d36adadffc3dbb23162df01f83e11e4d504fa8afc1",
127
+ "updated": "2026-10-12T00:35:07Z"
128
128
  },
129
129
  {
130
130
  "file": "serverless-functions-js.md",
@@ -148,8 +148,8 @@
148
148
  },
149
149
  {
150
150
  "file": "what-a-game-must-do.md",
151
- "sha256": "ce2abba4b32ee1c0dd69eabaaac1f33e2534ac5b58e9a45c4f8a7b3bd3e11f27",
152
- "updated": "2026-10-11T19:25:48Z"
151
+ "sha256": "ab4f18f2c1cfccf54b01027dea77d229f0a9f086e373d6cfd4c741fd0004557f",
152
+ "updated": "2026-10-11T19:48:44Z"
153
153
  }
154
154
  ]
155
155
  }