@indiegems/gem-web-sdk 10.3.1 → 11.0.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.3.1"
5
+ "version": "11.0.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",
@@ -8702,7 +8805,7 @@
8702
8805
  "group": "player",
8703
8806
  "signature": "readonly recommended: GemRecommendedSettings | null",
8704
8807
  "documented": true,
8705
- "summary": "The player's Arcade-wide defaults for this device — a volume, battery\nsaver, a keyboard layout, the gamepad's confirm button, vibration, and the\ncamera and controls settings (look sensitivity, gamepad look sensitivity,\ninvert look, touch movement) — or `null` when the Arcade did not send them.\n\n**Recommendations only; the player's own choice in your game wins.** Start\na setting from here while the player has made no choice of their own for\nit, and never overwrite one they made because this changed:\n\n```js\nconst saved = gem.deviceSettings.current ?? {};\nconst volume = saved.volume ?? gem.prefs?.recommended?.volume ?? 100;\n```\n\nUnlike `muted` and `reducedMotion`, nothing here is applied for you, and\nnothing obliges you to use it. See the guide's \"Game settings\" section.",
8808
+ "summary": "The player's Arcade-wide defaults for this device — a volume, battery\nsaver, a keyboard layout, the gamepad's confirm button, vibration, the\ncamera and controls settings (look sensitivity, gamepad look sensitivity,\ninvert look, touch movement) and a starting graphics quality — or `null`\nwhen the Arcade did not send them.\n\n**Recommendations only; the player's own choice in your game wins.** Start\na setting from here while the player has made no choice of their own for\nit, and never overwrite one they made because this changed:\n\n```js\nconst saved = gem.deviceSettings.current ?? {};\nconst volume = saved.volume ?? gem.prefs?.recommended?.volume ?? 100;\n```\n\nUnlike `muted` and `reducedMotion`, nothing here is applied for you, and\nnothing obliges you to use it. See the guide's \"Game settings\" section.",
8706
8809
  "deprecated": null,
8707
8810
  "params": [],
8708
8811
  "returns": null,
@@ -8876,6 +8979,38 @@
8876
8979
  "defaultValue": null,
8877
8980
  "guides": []
8878
8981
  },
8982
+ {
8983
+ "id": "GemRecommendedSettings.graphicsQuality",
8984
+ "name": "graphicsQuality",
8985
+ "anchor": "gemrecommendedsettings-graphicsquality",
8986
+ "group": "player",
8987
+ "signature": "readonly graphicsQuality: GemGraphicsQuality",
8988
+ "documented": true,
8989
+ "summary": "The graphics quality to START at on this device: `'low'`, `'medium'` or\n`'high'`. The Arcade picks it per device — the tier it detected, or the\nplayer's own Graphics choice — so a game opens at a quality that suits the\ndevice instead of guessing. A starting point, not a cap: while\n`graphicsQualityAuto` is true your game may adapt from here as it runs,\nand `createQualityGovernor` from `@indiegems/gem-web-sdk/quality` does\nthat for you. When the Arcade did not say, `'low'` with `batterySaver`\non and `'medium'` otherwise.",
8990
+ "deprecated": null,
8991
+ "params": [],
8992
+ "returns": null,
8993
+ "throws": null,
8994
+ "example": null,
8995
+ "defaultValue": null,
8996
+ "guides": []
8997
+ },
8998
+ {
8999
+ "id": "GemRecommendedSettings.graphicsQualityAuto",
9000
+ "name": "graphicsQualityAuto",
9001
+ "anchor": "gemrecommendedsettings-graphicsqualityauto",
9002
+ "group": "player",
9003
+ "signature": "readonly graphicsQualityAuto: boolean",
9004
+ "documented": true,
9005
+ "summary": "Whether the Arcade chose `graphicsQuality` for this device (`true`, the\nplayer left Graphics on Auto) or the player picked it by hand (`false`).\nWhile it is `false`, hold that quality: never step it down or up on your\nown. `true` when the Arcade did not say.",
9006
+ "deprecated": null,
9007
+ "params": [],
9008
+ "returns": null,
9009
+ "throws": null,
9010
+ "example": null,
9011
+ "defaultValue": null,
9012
+ "guides": []
9013
+ },
8879
9014
  {
8880
9015
  "id": "GemCohort",
8881
9016
  "name": "GemCohort",
@@ -9724,11 +9859,18 @@
9724
9859
  "name": "openAvatarPicker",
9725
9860
  "anchor": "gem-openavatarpicker",
9726
9861
  "group": "start",
9727
- "signature": "openAvatarPicker(): boolean",
9862
+ "signature": "openAvatarPicker(options?: { tab?: \"2d\" | \"3d\" }): boolean",
9728
9863
  "documented": true,
9729
- "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.",
9730
9865
  "deprecated": null,
9731
- "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
+ ],
9732
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.",
9733
9875
  "throws": null,
9734
9876
  "example": null,
@@ -9949,6 +10091,22 @@
9949
10091
  "defaultValue": null,
9950
10092
  "guides": []
9951
10093
  },
10094
+ {
10095
+ "id": "Gem.paused",
10096
+ "name": "paused",
10097
+ "anchor": "gem-paused",
10098
+ "group": "start",
10099
+ "signature": "readonly paused: boolean",
10100
+ "documented": true,
10101
+ "summary": "Whether the arcade has your game paused right now: `true` from an\n`onPause(true)` until the `onPause(false)` that follows it, `false` before\nany. The same pause `onPause` tells you about, readable at any moment —\nfor code that was not around when the edge arrived, such as a frame-time\nmonitor deciding whether a slow frame counts.",
10102
+ "deprecated": null,
10103
+ "params": [],
10104
+ "returns": null,
10105
+ "throws": null,
10106
+ "example": null,
10107
+ "defaultValue": null,
10108
+ "guides": []
10109
+ },
9952
10110
  {
9953
10111
  "id": "Gem.cohorts",
9954
10112
  "name": "cohorts",
@@ -11371,6 +11529,54 @@
11371
11529
  "defaultValue": null,
11372
11530
  "guides": []
11373
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
+ },
11374
11580
  {
11375
11581
  "id": "PlayerIdentity.badges",
11376
11582
  "name": "badges",
@@ -11394,7 +11600,7 @@
11394
11600
  "group": "player",
11395
11601
  "signature": "function mapIdentity(row: unknown): PlayerIdentity",
11396
11602
  "documented": true,
11397
- "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.",
11398
11604
  "deprecated": null,
11399
11605
  "params": [
11400
11606
  {
@@ -11417,7 +11623,7 @@
11417
11623
  "group": "player",
11418
11624
  "signature": "function platformIconUrl(url: string | undefined): string | undefined",
11419
11625
  "documented": true,
11420
- "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.",
11421
11627
  "deprecated": null,
11422
11628
  "params": [
11423
11629
  {
@@ -12778,6 +12984,22 @@
12778
12984
  "defaultValue": null,
12779
12985
  "guides": []
12780
12986
  },
12987
+ {
12988
+ "id": "GemGraphicsQuality",
12989
+ "name": "GemGraphicsQuality",
12990
+ "anchor": "gemgraphicsquality",
12991
+ "group": "player",
12992
+ "signature": "type GemGraphicsQuality = \"low\" | \"medium\" | \"high\"",
12993
+ "documented": true,
12994
+ "summary": "A graphics quality tier: `'low'`, `'medium'` or `'high'`. What\n`gem.prefs.recommended.graphicsQuality` holds.",
12995
+ "deprecated": null,
12996
+ "params": [],
12997
+ "returns": null,
12998
+ "throws": null,
12999
+ "example": null,
13000
+ "defaultValue": null,
13001
+ "guides": []
13002
+ },
12781
13003
  {
12782
13004
  "id": "ServerTokenGrant",
12783
13005
  "name": "ServerTokenGrant",
@@ -14143,6 +14365,54 @@
14143
14365
  "defaultValue": null,
14144
14366
  "guides": []
14145
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
+ },
14146
14416
  {
14147
14417
  "id": "LeaderboardEntry.badges",
14148
14418
  "name": "badges",
@@ -15906,6 +16176,54 @@
15906
16176
  "defaultValue": null,
15907
16177
  "guides": []
15908
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
+ },
15909
16227
  {
15910
16228
  "id": "PlayerProfile.badges",
15911
16229
  "name": "badges",
@@ -18722,6 +19040,54 @@
18722
19040
  "defaultValue": null,
18723
19041
  "guides": []
18724
19042
  },
19043
+ {
19044
+ "id": "RoomMember.avatar3dId",
19045
+ "name": "avatar3dId",
19046
+ "anchor": "roommember-avatar3did",
19047
+ "group": "rooms",
19048
+ "signature": "readonly avatar3dId?: string",
19049
+ "documented": true,
19050
+ "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`.",
19051
+ "deprecated": null,
19052
+ "params": [],
19053
+ "returns": null,
19054
+ "throws": null,
19055
+ "example": null,
19056
+ "defaultValue": null,
19057
+ "guides": []
19058
+ },
19059
+ {
19060
+ "id": "RoomMember.avatar3dModelUrl",
19061
+ "name": "avatar3dModelUrl",
19062
+ "anchor": "roommember-avatar3dmodelurl",
19063
+ "group": "rooms",
19064
+ "signature": "readonly avatar3dModelUrl?: string",
19065
+ "documented": true,
19066
+ "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.",
19067
+ "deprecated": null,
19068
+ "params": [],
19069
+ "returns": null,
19070
+ "throws": null,
19071
+ "example": null,
19072
+ "defaultValue": null,
19073
+ "guides": []
19074
+ },
19075
+ {
19076
+ "id": "RoomMember.avatar3dIconUrl",
19077
+ "name": "avatar3dIconUrl",
19078
+ "anchor": "roommember-avatar3diconurl",
19079
+ "group": "rooms",
19080
+ "signature": "readonly avatar3dIconUrl?: string",
19081
+ "documented": true,
19082
+ "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>`.",
19083
+ "deprecated": null,
19084
+ "params": [],
19085
+ "returns": null,
19086
+ "throws": null,
19087
+ "example": null,
19088
+ "defaultValue": null,
19089
+ "guides": []
19090
+ },
18725
19091
  {
18726
19092
  "id": "RoomMember.badges",
18727
19093
  "name": "badges",
@@ -19804,6 +20170,54 @@
19804
20170
  "defaultValue": null,
19805
20171
  "guides": []
19806
20172
  },
20173
+ {
20174
+ "id": "ShowcaseFriendEntry.avatar3dId",
20175
+ "name": "avatar3dId",
20176
+ "anchor": "showcasefriendentry-avatar3did",
20177
+ "group": "showcase",
20178
+ "signature": "readonly avatar3dId?: string",
20179
+ "documented": true,
20180
+ "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`.",
20181
+ "deprecated": null,
20182
+ "params": [],
20183
+ "returns": null,
20184
+ "throws": null,
20185
+ "example": null,
20186
+ "defaultValue": null,
20187
+ "guides": []
20188
+ },
20189
+ {
20190
+ "id": "ShowcaseFriendEntry.avatar3dModelUrl",
20191
+ "name": "avatar3dModelUrl",
20192
+ "anchor": "showcasefriendentry-avatar3dmodelurl",
20193
+ "group": "showcase",
20194
+ "signature": "readonly avatar3dModelUrl?: string",
20195
+ "documented": true,
20196
+ "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.",
20197
+ "deprecated": null,
20198
+ "params": [],
20199
+ "returns": null,
20200
+ "throws": null,
20201
+ "example": null,
20202
+ "defaultValue": null,
20203
+ "guides": []
20204
+ },
20205
+ {
20206
+ "id": "ShowcaseFriendEntry.avatar3dIconUrl",
20207
+ "name": "avatar3dIconUrl",
20208
+ "anchor": "showcasefriendentry-avatar3diconurl",
20209
+ "group": "showcase",
20210
+ "signature": "readonly avatar3dIconUrl?: string",
20211
+ "documented": true,
20212
+ "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>`.",
20213
+ "deprecated": null,
20214
+ "params": [],
20215
+ "returns": null,
20216
+ "throws": null,
20217
+ "example": null,
20218
+ "defaultValue": null,
20219
+ "guides": []
20220
+ },
19807
20221
  {
19808
20222
  "id": "ShowcaseFriendEntry.badges",
19809
20223
  "name": "badges",
package/docs/changelog.md CHANGED
@@ -16,6 +16,37 @@ 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.0.0
20
+
21
+ Upgrading from 10.4.0. Impact: required.
22
+
23
+ ### Rebuild
24
+
25
+ - 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.
26
+ - 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.
27
+
28
+ ### New capability
29
+
30
+ - 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.
31
+ - 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.
32
+ - 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.
33
+ - 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.
34
+ - 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.
35
+
36
+ ## 10.4.0
37
+
38
+ Upgrading from 10.3.1. Impact: required.
39
+
40
+ ### Rebuild
41
+
42
+ - The recommended preferences type gained optional graphics quality and automatic fields, and the host method that sends preferences accepts the same two fields. Existing code keeps working; recompile your game against the new types. Host or test-harness code that sends preferences can now also pass the graphics quality tier and the automatic flag.
43
+
44
+ ### New capability
45
+
46
+ - The recommended settings now carry a graphics quality tier and an automatic flag. The Arcade picks a starting tier per device (low, medium or high) and a game can start from it. An unknown tier is read as medium, a missing tier follows the battery saver setting (low when it is on, otherwise medium), and a missing automatic flag is read as true, so both values are always present in the game. When the automatic flag is false the player picked the tier by hand, and the game should keep it rather than adapt it.
47
+ - The new paused value holds the latest state of the pause signal. Code that starts after the pause signal has already arrived, such as a frame-time monitor created mid-game, can read it to learn whether the game is currently paused.
48
+ - A new optional quality entry point lets a game adapt its graphics level to frame time. It starts at the Arcade's tier mapped onto the game's own levels. It steps down one level after about two seconds of slow frames and up one level after about ten seconds of comfortably fast frames, never going above one level over its start. It ignores paused and hidden time, holds steady when the player chose the tier by hand, and restarts when a new recommendation arrives. It can run on animation frames or be fed frame times manually. A game that does not import it pays nothing in size.
49
+
19
50
  ## 10.3.1
20
51
 
21
52
  Upgrading from 10.3.0. Impact: none.
@@ -50,7 +50,7 @@ either way.
50
50
  ```jsonc
51
51
  // package.json
52
52
  "dependencies": {
53
- "@indiegems/gem-web-sdk": "10.3.1"
53
+ "@indiegems/gem-web-sdk": "11.0.0"
54
54
  }
55
55
  ```
56
56