@diffenderfer-games/hub 2.0.0-next.0 → 2.0.0-next.10

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.
Files changed (293) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/LICENSE +21 -21
  3. package/README.md +203 -139
  4. package/dist/auto.d.ts +8 -8
  5. package/dist/auto.js +3 -3
  6. package/dist/avatar-pixi.d.ts +165 -0
  7. package/dist/avatar-pixi.d.ts.map +1 -0
  8. package/dist/avatar-pixi.js +299 -0
  9. package/dist/avatar-pixi.js.map +1 -0
  10. package/dist/avatar-svg.d.ts +4 -0
  11. package/dist/avatar-svg.js +5 -0
  12. package/dist/avatar-three.d.ts +69 -0
  13. package/dist/avatar-three.d.ts.map +1 -0
  14. package/dist/avatar-three.js +49 -0
  15. package/dist/avatar-three.js.map +1 -0
  16. package/dist/avatar.d.ts +7 -0
  17. package/dist/avatar.js +500 -0
  18. package/dist/avatar.js.map +1 -0
  19. package/dist/daily.d.ts +2 -2
  20. package/dist/doctor/cli.mjs +119 -28
  21. package/dist/doctor/cli.mjs.map +1 -1
  22. package/dist/index.d.ts +8 -8
  23. package/dist/index.js +3 -3
  24. package/dist/input.d.ts +2 -2
  25. package/dist/input.js +2 -2
  26. package/dist/mp.d.ts +3 -3
  27. package/dist/race.d.ts +1 -1
  28. package/dist/rooms.d.ts +3 -3
  29. package/dist/rooms.js +1 -1
  30. package/dist/shared/animator.d.ts +104 -0
  31. package/dist/shared/animator.d.ts.map +1 -0
  32. package/dist/shared/art.js +1946 -0
  33. package/dist/shared/art.js.map +1 -0
  34. package/dist/shared/atlas.d.ts +138 -0
  35. package/dist/shared/atlas.d.ts.map +1 -0
  36. package/dist/shared/avatar.d.ts +123 -0
  37. package/dist/shared/avatar.d.ts.map +1 -0
  38. package/dist/shared/bridge.js +18 -4
  39. package/dist/shared/bridge.js.map +1 -1
  40. package/dist/shared/client-version.gen.js +1 -1
  41. package/dist/shared/daily.d.ts +145 -19
  42. package/dist/shared/daily.d.ts.map +1 -1
  43. package/dist/shared/daily.js +138 -25
  44. package/dist/shared/daily.js.map +1 -1
  45. package/dist/shared/defineProperty.js +39 -0
  46. package/dist/shared/describe.d.ts +32 -0
  47. package/dist/shared/describe.d.ts.map +1 -0
  48. package/dist/shared/describe.js +109 -0
  49. package/dist/shared/describe.js.map +1 -0
  50. package/dist/shared/globals.js +9 -42
  51. package/dist/shared/globals.js.map +1 -1
  52. package/dist/shared/index.d.ts +1122 -46
  53. package/dist/shared/index.d.ts.map +1 -1
  54. package/dist/shared/input.d.ts +74 -14
  55. package/dist/shared/input.d.ts.map +1 -1
  56. package/dist/shared/input.js +179 -25
  57. package/dist/shared/input.js.map +1 -1
  58. package/dist/shared/json-guards.js +48 -0
  59. package/dist/shared/json-guards.js.map +1 -0
  60. package/dist/shared/latest-state.js +1 -1
  61. package/dist/shared/live-canvas.js +62 -0
  62. package/dist/shared/live-canvas.js.map +1 -0
  63. package/dist/shared/mp.js +10 -2
  64. package/dist/shared/mp.js.map +1 -1
  65. package/dist/shared/multiplayer-types.d.ts +15 -2
  66. package/dist/shared/multiplayer-types.d.ts.map +1 -1
  67. package/dist/shared/overlays.js +800 -0
  68. package/dist/shared/overlays.js.map +1 -0
  69. package/dist/shared/page-stack.js +2 -1
  70. package/dist/shared/page-stack.js.map +1 -1
  71. package/dist/shared/pose-key.js +249 -0
  72. package/dist/shared/pose-key.js.map +1 -0
  73. package/dist/shared/race-types.d.ts +1 -1
  74. package/dist/shared/race.js +1 -1
  75. package/dist/shared/room-types.d.ts +28 -3
  76. package/dist/shared/room-types.d.ts.map +1 -1
  77. package/dist/shared/rooms.d.ts.map +1 -1
  78. package/dist/shared/rooms.js +23 -4
  79. package/dist/shared/rooms.js.map +1 -1
  80. package/dist/shared/runtime-opener.js +1 -1
  81. package/dist/shared/scene.d.ts +90 -0
  82. package/dist/shared/scene.d.ts.map +1 -0
  83. package/dist/shared/social.d.ts +90 -9
  84. package/dist/shared/social.d.ts.map +1 -1
  85. package/dist/shared/social.js +6 -1
  86. package/dist/shared/social.js.map +1 -1
  87. package/dist/shared/src.js +4563 -667
  88. package/dist/shared/src.js.map +1 -1
  89. package/dist/shared/svg-markup.js +2354 -0
  90. package/dist/shared/svg-markup.js.map +1 -0
  91. package/dist/shared/svg.js +27 -0
  92. package/dist/shared/svg.js.map +1 -0
  93. package/dist/shared/types.d.ts +174 -0
  94. package/dist/shared/types.d.ts.map +1 -0
  95. package/dist/shared/wire-types.d.ts +6 -2
  96. package/dist/shared/wire-types.d.ts.map +1 -1
  97. package/dist/social.d.ts +5 -5
  98. package/dist/social.js +2 -2
  99. package/dist/turns-rules.d.ts +2 -0
  100. package/dist/turns-rules.js +1 -0
  101. package/dist/turns.d.ts +61 -0
  102. package/dist/turns.d.ts.map +1 -0
  103. package/dist/turns.js +183 -0
  104. package/dist/turns.js.map +1 -0
  105. package/package.json +125 -89
  106. package/src/auto.ts +39 -39
  107. package/src/avatar/adapters/pixi-avatar.ts +73 -0
  108. package/src/avatar/adapters/pixi-crowd.ts +329 -0
  109. package/src/avatar/adapters/pixi-types.ts +84 -0
  110. package/src/avatar/adapters/three.ts +126 -0
  111. package/src/avatar/animator/animator.ts +285 -0
  112. package/src/avatar/animator/reactions.ts +44 -0
  113. package/src/avatar/animator/schedule.ts +73 -0
  114. package/src/avatar/art/beard.ts +49 -0
  115. package/src/avatar/art/glasses.ts +67 -0
  116. package/src/avatar/art/hair.ts +254 -0
  117. package/src/avatar/art/headwear.ts +75 -0
  118. package/src/avatar/art/index.ts +21 -0
  119. package/src/avatar/art/shape.ts +87 -0
  120. package/src/avatar/art-store.ts +36 -0
  121. package/src/avatar/atlas.ts +246 -0
  122. package/src/avatar/catalog-view.ts +14 -0
  123. package/src/avatar/colour.ts +109 -0
  124. package/src/avatar/describe.ts +79 -0
  125. package/src/avatar/face.ts +187 -0
  126. package/src/avatar/geom.ts +173 -0
  127. package/src/avatar/layout.ts +47 -0
  128. package/src/avatar/look-like.ts +52 -0
  129. package/src/avatar/outputs/bitmap.ts +75 -0
  130. package/src/avatar/outputs/canvas-paint.ts +177 -0
  131. package/src/avatar/outputs/canvas.ts +62 -0
  132. package/src/avatar/outputs/custom-element.ts +140 -0
  133. package/src/avatar/outputs/element.ts +58 -0
  134. package/src/avatar/outputs/live-canvas.ts +57 -0
  135. package/src/avatar/outputs/overlay-svg.ts +22 -0
  136. package/src/avatar/outputs/pose-key.ts +30 -0
  137. package/src/avatar/outputs/svg-markup.ts +113 -0
  138. package/src/avatar/outputs/svg.ts +41 -0
  139. package/src/avatar/overlays.ts +128 -0
  140. package/src/avatar/render/body.ts +116 -0
  141. package/src/avatar/render/colours.ts +81 -0
  142. package/src/avatar/render/compose.ts +320 -0
  143. package/src/avatar/render/context.ts +117 -0
  144. package/src/avatar/render/eyes.ts +78 -0
  145. package/src/avatar/render/eyewear.ts +118 -0
  146. package/src/avatar/render/frame.ts +73 -0
  147. package/src/avatar/render/head.ts +81 -0
  148. package/src/avatar/render/mouth.ts +86 -0
  149. package/src/avatar/render/props.ts +91 -0
  150. package/src/avatar/render/types.ts +93 -0
  151. package/src/avatar/render/walker-parts.ts +121 -0
  152. package/src/avatar/render/walker.ts +199 -0
  153. package/src/avatar/scene.ts +119 -0
  154. package/src/avatar-pixi.ts +20 -0
  155. package/src/avatar-svg.ts +21 -0
  156. package/src/avatar-three.ts +18 -0
  157. package/src/avatar.ts +63 -0
  158. package/src/core/avatar/avatar-cache.ts +95 -0
  159. package/src/core/avatar/avatar-ref.ts +34 -0
  160. package/src/core/avatar/avatar-types.ts +43 -0
  161. package/src/core/avatar/hub-avatar.ts +308 -0
  162. package/src/core/connection.ts +408 -299
  163. package/src/core/errors.ts +160 -147
  164. package/src/core/game-data.ts +40 -38
  165. package/src/core/hub-economy.ts +216 -179
  166. package/src/core/hub-progress.ts +269 -205
  167. package/src/core/hub.ts +352 -131
  168. package/src/core/menu-control.ts +61 -61
  169. package/src/core/offline/cloud-saves.ts +62 -0
  170. package/src/core/offline/events.ts +50 -50
  171. package/src/core/offline/favorites.ts +21 -21
  172. package/src/core/offline/hub-kinds/avatar-kind.ts +42 -0
  173. package/src/core/offline/hub-kinds/daily-kind.ts +68 -0
  174. package/src/core/offline/hub-kinds/economy-kinds.ts +86 -0
  175. package/src/core/offline/hub-kinds/game-fields.ts +2 -0
  176. package/src/core/offline/hub-kinds/index.ts +52 -0
  177. package/src/core/offline/hub-kinds/legacy-ops.ts +37 -0
  178. package/src/core/offline/hub-kinds/progress-kinds.ts +165 -0
  179. package/src/core/offline/hub-kinds/resume-kind.ts +108 -0
  180. package/src/core/offline/hub-kinds/saved-kind.ts +93 -0
  181. package/src/core/offline/kind-adapters.ts +48 -0
  182. package/src/core/offline/local-inventory.ts +64 -0
  183. package/src/core/offline/local-store.ts +304 -261
  184. package/src/core/offline/offline-api.ts +65 -0
  185. package/src/core/offline/outbox-entry.ts +59 -0
  186. package/src/core/offline/outbox-kinds.ts +78 -0
  187. package/src/core/offline/outbox-merge.ts +39 -0
  188. package/src/core/offline/outbox-replayer.ts +141 -0
  189. package/src/core/offline/outbox-store.ts +85 -0
  190. package/src/core/offline/outbox.ts +232 -0
  191. package/src/core/offline/queue.ts +126 -171
  192. package/src/core/offline/replay-lease.ts +87 -0
  193. package/src/core/offline/replay-policy.ts +52 -0
  194. package/src/core/page-account.ts +121 -0
  195. package/src/core/page.ts +87 -74
  196. package/src/core/rest/endpoint-types.ts +22 -22
  197. package/src/core/rest/rest-client.ts +163 -152
  198. package/src/core/rest/routes.gen.ts +144 -88
  199. package/src/core/resume/hub-resume.ts +286 -0
  200. package/src/core/resume/local-resume.ts +130 -0
  201. package/src/core/resume/resume-list.ts +65 -0
  202. package/src/core/resume/resume-request.ts +52 -0
  203. package/src/core/resume/resume-types.ts +64 -0
  204. package/src/core/saved/daily-saves.ts +196 -0
  205. package/src/core/saved/daily-seed.ts +16 -0
  206. package/src/core/saved/hub-saved.ts +221 -0
  207. package/src/core/saved/local-saved.ts +122 -0
  208. package/src/core/saved/saved-list.ts +129 -0
  209. package/src/core/saved/saved-open.ts +104 -0
  210. package/src/core/saved/saved-order.ts +49 -0
  211. package/src/core/saved/saved-types.ts +24 -0
  212. package/src/core/storage/json-guards.ts +80 -74
  213. package/src/core/storage/key-value.ts +66 -66
  214. package/src/core/ticket.ts +43 -43
  215. package/src/core/turns/hub-turns.ts +161 -0
  216. package/src/core/turns/turn-game.ts +510 -0
  217. package/src/core/turns/your-turn-buzz.ts +69 -0
  218. package/src/core/types.ts +134 -134
  219. package/src/core/version/client-version.gen.ts +2 -2
  220. package/src/core/version/hub-version.ts +62 -62
  221. package/src/core/version/semver.ts +97 -97
  222. package/src/daily/daily-system.ts +276 -161
  223. package/src/daily/page-daily.ts +36 -36
  224. package/src/daily/types.ts +105 -67
  225. package/src/daily.ts +10 -10
  226. package/src/doctor/checks.ts +86 -76
  227. package/src/doctor/cli.ts +101 -106
  228. package/src/doctor/hub-version-fetch.ts +43 -0
  229. package/src/doctor/tsconfig.json +12 -12
  230. package/src/doctor/world-check.ts +61 -0
  231. package/src/globals.ts +115 -110
  232. package/src/index.ts +68 -50
  233. package/src/input/device-state.ts +146 -146
  234. package/src/input/edges.ts +55 -55
  235. package/src/input/focus-ring.ts +13 -0
  236. package/src/input/gamepad.ts +61 -61
  237. package/src/input/input-events.ts +32 -32
  238. package/src/input/input-registry.ts +171 -164
  239. package/src/input/input-system.ts +406 -393
  240. package/src/input/legacy-controls.ts +64 -64
  241. package/src/input/modality.ts +122 -0
  242. package/src/input/nav-state.ts +200 -191
  243. package/src/input/navigator.ts +182 -171
  244. package/src/input/on-screen-controls.ts +101 -101
  245. package/src/input/page-control.ts +43 -43
  246. package/src/input/page-input.ts +193 -193
  247. package/src/input/player-remaps.ts +77 -77
  248. package/src/input/source-values.ts +116 -116
  249. package/src/input/spatial.ts +47 -47
  250. package/src/input/types.ts +331 -331
  251. package/src/input/zero-config.ts +164 -177
  252. package/src/input.ts +26 -22
  253. package/src/mp.ts +10 -10
  254. package/src/overlay/controls-panel.ts +163 -163
  255. package/src/overlay/dom.ts +46 -46
  256. package/src/overlay/layout-clusters.ts +162 -162
  257. package/src/overlay/layout-geometry.ts +136 -136
  258. package/src/overlay/layout.ts +203 -203
  259. package/src/overlay/listener-scope.ts +23 -23
  260. package/src/overlay/on-screen-keyboard.ts +137 -137
  261. package/src/overlay/overlay.ts +234 -208
  262. package/src/overlay/styles.ts +13 -12
  263. package/src/overlay/virtual-controls.ts +262 -262
  264. package/src/race.ts +10 -10
  265. package/src/realtime/bridge.ts +126 -121
  266. package/src/realtime/cross-bundle.ts +29 -29
  267. package/src/realtime/fallback-notice.ts +48 -48
  268. package/src/realtime/hub-link.ts +65 -65
  269. package/src/realtime/latest-state.ts +33 -33
  270. package/src/realtime/multiplayer-forwarder.ts +40 -28
  271. package/src/realtime/multiplayer-types.ts +66 -52
  272. package/src/realtime/page-runtime.ts +60 -60
  273. package/src/realtime/race-forwarder.ts +59 -59
  274. package/src/realtime/race-types.ts +132 -132
  275. package/src/realtime/room-types.ts +151 -125
  276. package/src/realtime/rooms-forwarder.ts +104 -79
  277. package/src/realtime/runtime-api.ts +74 -49
  278. package/src/realtime/runtime-opener.ts +61 -61
  279. package/src/realtime/social-forwarder.ts +77 -72
  280. package/src/realtime/social-types.ts +86 -78
  281. package/src/realtime/types.ts +54 -53
  282. package/src/realtime/wire-types.ts +79 -72
  283. package/src/realtime/world-types.ts +20 -0
  284. package/src/rooms.ts +10 -10
  285. package/src/social.ts +19 -18
  286. package/src/turns-kit/check-rules.ts +281 -0
  287. package/src/turns-rules.ts +12 -0
  288. package/src/turns.ts +14 -0
  289. package/src/types.ts +13 -13
  290. package/src/uistack/page-stack.ts +45 -45
  291. package/src/uistack/play-suspension.ts +82 -82
  292. package/src/uistack/types.ts +76 -76
  293. package/src/uistack/ui-stack.ts +144 -144
@@ -1,79 +1,104 @@
1
- /**
2
- * `hub.rooms`: hub relay rooms, forwarded to the social runtime. Each room is
3
- * wrapped so its rejections use the core client's error classes.
4
- */
5
- import type { Json, RoomInfo } from '@diffenderfer-games/hub-contract';
6
- import { pageBridge, rethrowAsHubError } from './bridge';
7
- import type { HubRoom, HubRoomsApi, RoomEvents, RoomSelf, RoomSendOptions, RoomState, RoomStateOptions } from './room-types';
8
-
9
- /** A runtime room whose rejections are re-created as the core client's errors. */
10
- class ForwardedRoom implements HubRoom {
11
- private readonly room: HubRoom;
12
-
13
- constructor(room: HubRoom) {
14
- this.room = room;
15
- }
16
-
17
- get info(): RoomInfo {
18
- return this.room.info;
19
- }
20
-
21
- get state(): RoomState {
22
- return this.room.state;
23
- }
24
-
25
- get me(): RoomSelf {
26
- return this.room.me;
27
- }
28
-
29
- on<Name extends keyof RoomEvents>(event: Name, listener: (payload: RoomEvents[Name]) => void): () => void {
30
- return this.room.on(event, listener);
31
- }
32
-
33
- ready(isReady: boolean): Promise<void> {
34
- return rethrowAsHubError(this.room.ready(isReady));
35
- }
36
-
37
- seat(seatData: Json): Promise<void> {
38
- return rethrowAsHubError(this.room.seat(seatData));
39
- }
40
-
41
- start(): Promise<void> {
42
- return rethrowAsHubError(this.room.start());
43
- }
44
-
45
- send(message: Json, options?: RoomSendOptions): Promise<void> {
46
- return rethrowAsHubError(this.room.send(message, options));
47
- }
48
-
49
- setState(patch: RoomState, options?: RoomStateOptions): Promise<void> {
50
- return rethrowAsHubError(this.room.setState(patch, options));
51
- }
52
-
53
- chat(text: string): Promise<string> {
54
- return rethrowAsHubError(this.room.chat(text));
55
- }
56
-
57
- quick(phraseId: string): Promise<void> {
58
- return rethrowAsHubError(this.room.quick(phraseId));
59
- }
60
-
61
- kick(userId: number): Promise<void> {
62
- return rethrowAsHubError(this.room.kick(userId));
63
- }
64
-
65
- leave(): Promise<void> {
66
- return rethrowAsHubError(this.room.leave());
67
- }
68
-
69
- end(results?: Json): Promise<void> {
70
- return rethrowAsHubError(this.room.end(results));
71
- }
72
- }
73
-
74
- /** `hub.rooms`: hub relay rooms. */
75
- export const roomsForwarder: HubRoomsApi = {
76
- create: (options) => pageBridge.call(async (realtime) => new ForwardedRoom(await realtime.rooms.create(options))),
77
- join: (codeOrId, options) => pageBridge.call(async (realtime) => new ForwardedRoom(await realtime.rooms.join(codeOrId, options))),
78
- list: (options) => pageBridge.call((realtime) => realtime.rooms.list(options)),
79
- };
1
+ /**
2
+ * `hub.rooms`: hub relay rooms, forwarded to the social runtime. Each room is
3
+ * wrapped so its rejections use the core client's error classes, and the
4
+ * page's open rooms are tracked (`isInHubRoom`), so `hub.resume` can refuse
5
+ * to save an online run.
6
+ */
7
+ import type { Json, RoomInfo } from '@diffenderfer-games/hub-contract';
8
+ import { outdatedRuntime, pageBridge, rethrowAsHubError } from './bridge';
9
+ import type { HubRoom, HubRoomsApi, RoomEvents, RoomSelf, RoomSendOptions, RoomState, RoomStateOptions } from './room-types';
10
+ import type { RoomSeat } from './wire-types';
11
+
12
+ /** The rooms this page is in (created, joined or reclaimed, and not yet left or closed). */
13
+ const openRooms = new Set<HubRoom>();
14
+
15
+ /** Whether this page is in a hub room right now. */
16
+ export function isInHubRoom(): boolean {
17
+ return openRooms.size > 0;
18
+ }
19
+
20
+ /** A runtime room whose rejections are re-created as the core client's errors. */
21
+ class ForwardedRoom implements HubRoom {
22
+ private readonly room: HubRoom;
23
+
24
+ constructor(room: HubRoom) {
25
+ this.room = room;
26
+ openRooms.add(room);
27
+ room.on('closed', () => { openRooms.delete(room); });
28
+ }
29
+
30
+ get info(): RoomInfo {
31
+ return this.room.info;
32
+ }
33
+
34
+ get state(): RoomState {
35
+ return this.room.state;
36
+ }
37
+
38
+ get me(): RoomSelf {
39
+ return this.room.me;
40
+ }
41
+
42
+ seats(): RoomSeat[] {
43
+ return this.room.info.seats;
44
+ }
45
+
46
+ on<Name extends keyof RoomEvents>(event: Name, listener: (payload: RoomEvents[Name]) => void): () => void {
47
+ return this.room.on(event, listener);
48
+ }
49
+
50
+ ready(isReady: boolean): Promise<void> {
51
+ return rethrowAsHubError(this.room.ready(isReady));
52
+ }
53
+
54
+ seat(seatData: Json): Promise<void> {
55
+ return rethrowAsHubError(this.room.seat(seatData));
56
+ }
57
+
58
+ start(): Promise<void> {
59
+ return rethrowAsHubError(this.room.start());
60
+ }
61
+
62
+ send(message: Json, options?: RoomSendOptions): Promise<void> {
63
+ return rethrowAsHubError(this.room.send(message, options));
64
+ }
65
+
66
+ setState(patch: RoomState, options?: RoomStateOptions): Promise<void> {
67
+ return rethrowAsHubError(this.room.setState(patch, options));
68
+ }
69
+
70
+ chat(text: string): Promise<string> {
71
+ return rethrowAsHubError(this.room.chat(text));
72
+ }
73
+
74
+ quick(phraseId: string): Promise<void> {
75
+ return rethrowAsHubError(this.room.quick(phraseId));
76
+ }
77
+
78
+ kick(userId: number): Promise<void> {
79
+ return rethrowAsHubError(this.room.kick(userId));
80
+ }
81
+
82
+ leave(): Promise<void> {
83
+ openRooms.delete(this.room);
84
+ return rethrowAsHubError(this.room.leave());
85
+ }
86
+
87
+ end(results?: Json): Promise<void> {
88
+ return rethrowAsHubError(this.room.end(results));
89
+ }
90
+ }
91
+
92
+ /** `hub.rooms`: hub relay rooms. */
93
+ export const roomsForwarder: HubRoomsApi = {
94
+ create: (options) => pageBridge.call(async (realtime) => new ForwardedRoom(await realtime.rooms.create(options))),
95
+ join: (codeOrId, options) => pageBridge.call(async (realtime) => new ForwardedRoom(await realtime.rooms.join(codeOrId, options))),
96
+ list: (options) => pageBridge.call((realtime) => realtime.rooms.list(options)),
97
+ reclaim: () => pageBridge.call(async (realtime) => {
98
+ if (realtime.rooms.reclaim === undefined) {
99
+ throw outdatedRuntime('Taking back a seat');
100
+ }
101
+ const held = await realtime.rooms.reclaim();
102
+ return held === null ? null : new ForwardedRoom(held);
103
+ }),
104
+ };
@@ -1,49 +1,74 @@
1
- /**
2
- * The realtime API the hub's social runtime (`/_hub/social.js`) installs on
3
- * the page, and that `hub.presence`, `hub.social`, `hub.mp`, `hub.rooms`,
4
- * `hub.notify` and `hub.race` forward to.
5
- */
6
- import type { HubMpApi } from './multiplayer-types';
7
- import type { HubRaceApi } from './race-types';
8
- import type { HubRoomsApi } from './room-types';
9
- import type { HubNotifyApi, HubPresenceApi, HubSocialApi } from './social-types';
10
-
11
- /**
12
- * The realtime feature version the runtime reports in {@link RealtimeApi.version}.
13
- * 2 added races; a client feature-detects newer additions with it.
14
- */
15
- export const REALTIME_FEATURE_VERSION = 2;
16
-
17
- /**
18
- * The cross-bundle shape number of the `rt` page singleton (plan §4.4). The
19
- * runtime registers with it through `claimSingleton`; bump it only when the
20
- * shape of {@link RealtimeApi} changes incompatibly.
21
- */
22
- export const REALTIME_SINGLETON_API = 1;
23
-
24
- /** The window event the runtime fires once it has installed itself. */
25
- export const REALTIME_READY_EVENT = 'hub:rt-ready';
26
-
27
- /** The window event the menu fires when the runtime failed to load (its reason is on `window.__HUB_RT_ERROR__`). */
28
- export const REALTIME_FAILED_EVENT = 'hub:rt-failed';
29
-
30
- /**
31
- * Installed by the social runtime as the page's `rt` singleton (and the v1
32
- * alias `window.__HUB_RT__`).
33
- */
34
- export interface RealtimeApi {
35
- /** The runtime's feature version (see {@link REALTIME_FEATURE_VERSION}). */
36
- readonly version: number;
37
- /** Presence reporting. */
38
- presence: HubPresenceApi;
39
- /** Friends and the social dialogs. */
40
- social: HubSocialApi;
41
- /** Invites, launches, the leave guard and tickets. */
42
- mp: HubMpApi;
43
- /** Hub relay rooms. */
44
- rooms: HubRoomsApi;
45
- /** Toast behaviour. */
46
- notify: HubNotifyApi;
47
- /** Races (feature version 2 and later). */
48
- race?: HubRaceApi;
49
- }
1
+ /**
2
+ * The realtime API the hub's social runtime (`/_hub/social.js`) installs on
3
+ * the page, and that `hub.presence`, `hub.social`, `hub.mp`, `hub.rooms`,
4
+ * `hub.notify` and `hub.race` forward to.
5
+ */
6
+ import type { RuntimeAvatarApi } from '../core/avatar/avatar-types';
7
+ import type { HubMpApi, InviteLinkMethods } from './multiplayer-types';
8
+ import type { HubRaceApi } from './race-types';
9
+ import type { HubRoomsApi, RoomReclaimMethods } from './room-types';
10
+ import type { HubNotifyApi, HubPresenceApi, HubSocialApi, PlayOnlineMethods } from './social-types';
11
+ import type { RuntimeWorldApi } from './world-types';
12
+
13
+ /**
14
+ * The realtime feature version the runtime reports in {@link RealtimeApi.version}.
15
+ * 2 added races; 3 added invite links and taking back a held seat; 4 added
16
+ * "Play online" (tournaments); 5 added avatars (the designer and `ev:avatar`); 6 added the world (`world`).
17
+ * A client feature-detects newer additions with it (or by the optional method).
18
+ */
19
+ export const REALTIME_FEATURE_VERSION = 6;
20
+
21
+ /** `hub.social` as a runtime installs it: version 4's `openPlayOnline` is missing on older runtimes. */
22
+ export type RuntimeSocialApi = Omit<HubSocialApi, keyof PlayOnlineMethods> & Partial<PlayOnlineMethods>;
23
+
24
+ /** `hub.mp` as a runtime installs it: version 3's invite-link methods are missing on older runtimes. */
25
+ export type RuntimeMpApi = Omit<HubMpApi, keyof InviteLinkMethods> & Partial<InviteLinkMethods>;
26
+
27
+ /** `hub.rooms` as a runtime installs it: version 3's `reclaim` is missing on older runtimes. */
28
+ export type RuntimeRoomsApi = Omit<HubRoomsApi, keyof RoomReclaimMethods> & Partial<RoomReclaimMethods>;
29
+
30
+ /**
31
+ * The cross-bundle shape number of the `rt` page singleton (plan §4.4). The
32
+ * runtime registers with it through `claimSingleton`; bump it only when the
33
+ * shape of {@link RealtimeApi} changes incompatibly.
34
+ */
35
+ export const REALTIME_SINGLETON_API = 1;
36
+
37
+ /** The window event the runtime fires once it has installed itself. */
38
+ export const REALTIME_READY_EVENT = 'hub:rt-ready';
39
+
40
+ /**
41
+ * The window event the runtime fires each time its socket says hello (it
42
+ * connected, or connected again after a drop), with `{ user: { id, look } }`
43
+ * from the hello snapshot. `hub.avatar` and `hub.userId()` follow it; changes
44
+ * sent while the socket was down are fetched again.
45
+ */
46
+ export const SESSION_EVENT = 'hub:session';
47
+
48
+ /** The window event the menu fires when the runtime failed to load (its reason is on `window.__HUB_RT_ERROR__`). */
49
+ export const REALTIME_FAILED_EVENT = 'hub:rt-failed';
50
+
51
+ /**
52
+ * Installed by the social runtime as the page's `rt` singleton (and the v1
53
+ * alias `window.__HUB_RT__`).
54
+ */
55
+ export interface RealtimeApi {
56
+ /** The runtime's feature version (see {@link REALTIME_FEATURE_VERSION}). */
57
+ readonly version: number;
58
+ /** Presence reporting. */
59
+ presence: HubPresenceApi;
60
+ /** Friends and the social dialogs. */
61
+ social: RuntimeSocialApi;
62
+ /** Invites, launches, the leave guard and tickets. */
63
+ mp: RuntimeMpApi;
64
+ /** Hub relay rooms. */
65
+ rooms: RuntimeRoomsApi;
66
+ /** Toast behaviour. */
67
+ notify: HubNotifyApi;
68
+ /** Races (feature version 2 and later). */
69
+ race?: HubRaceApi;
70
+ /** Avatars: the designer and live changes (feature version 5 and later). */
71
+ avatar?: RuntimeAvatarApi;
72
+ /** The world's ops and events (feature version 6 and later). */
73
+ world?: RuntimeWorldApi;
74
+ }
@@ -1,61 +1,61 @@
1
- /**
2
- * Opening the runtime's UI (a player card, the friends drawer, the race
3
- * sheet) from a game button. A tap must never look dead: if the runtime isn't
4
- * there within a moment the player is told it is loading, and if it never
5
- * arrives, how to recover.
6
- */
7
- import type { RealtimeBridge } from './bridge';
8
- import { hideFallbackNotice, showFallbackNotice } from './fallback-notice';
9
- import { readRuntimeFailure } from './page-runtime';
10
- import type { RealtimeApi } from './runtime-api';
11
- import { REALTIME_FAILED_EVENT } from './runtime-api';
12
-
13
- /** How long before "still loading" shows. */
14
- const SLOW_NOTICE_MS = 1500;
15
- /** How long the failure notice stays up. */
16
- const FAILURE_NOTICE_MS = 8000;
17
-
18
- /** Tell the player the social runtime failed, with the reason the menu recorded. */
19
- function showRuntimeFailure(): void {
20
- const reason = readRuntimeFailure();
21
- showFallbackNotice(`Friends couldn't load${reason === '' ? '' : ` (${reason})`}. Reload the page to try again.`, FAILURE_NOTICE_MS);
22
- }
23
-
24
- /** Make an opener: runs a UI call on the runtime once it is up, with the loading and failure notices. */
25
- export function createRuntimeOpener(bridge: RealtimeBridge): (openUi: (realtime: RealtimeApi) => void) => void {
26
- return (openUi) => {
27
- const loaded = bridge.current();
28
- if (loaded !== null) {
29
- openUi(loaded);
30
- return;
31
- }
32
- // The menu already knows the runtime failed.
33
- if (readRuntimeFailure() !== '') {
34
- showRuntimeFailure();
35
- return;
36
- }
37
- const slowTimer = setTimeout(() => {
38
- showFallbackNotice('Friends are still loading…');
39
- }, SLOW_NOTICE_MS);
40
- let isSettled = false;
41
- const onFailed = (): void => {
42
- if (!isSettled) {
43
- isSettled = true;
44
- clearTimeout(slowTimer);
45
- showRuntimeFailure();
46
- }
47
- };
48
- if (typeof window !== 'undefined') {
49
- window.addEventListener(REALTIME_FAILED_EVENT, onFailed, { once: true });
50
- }
51
- bridge.whenReady().then((realtime) => {
52
- if (isSettled) {
53
- return;
54
- }
55
- isSettled = true;
56
- clearTimeout(slowTimer);
57
- hideFallbackNotice();
58
- openUi(realtime);
59
- }, onFailed);
60
- };
61
- }
1
+ /**
2
+ * Opening the runtime's UI (a player card, the friends drawer, the race
3
+ * sheet) from a game button. A tap must never look dead: if the runtime isn't
4
+ * there within a moment the player is told it is loading, and if it never
5
+ * arrives, how to recover.
6
+ */
7
+ import type { RealtimeBridge } from './bridge';
8
+ import { hideFallbackNotice, showFallbackNotice } from './fallback-notice';
9
+ import { readRuntimeFailure } from './page-runtime';
10
+ import type { RealtimeApi } from './runtime-api';
11
+ import { REALTIME_FAILED_EVENT } from './runtime-api';
12
+
13
+ /** How long before "still loading" shows. */
14
+ const SLOW_NOTICE_MS = 1500;
15
+ /** How long the failure notice stays up. */
16
+ const FAILURE_NOTICE_MS = 8000;
17
+
18
+ /** Tell the player the social runtime failed, with the reason the menu recorded. */
19
+ function showRuntimeFailure(): void {
20
+ const reason = readRuntimeFailure();
21
+ showFallbackNotice(`Friends couldn't load${reason === '' ? '' : ` (${reason})`}. Reload the page to try again.`, FAILURE_NOTICE_MS);
22
+ }
23
+
24
+ /** Make an opener: runs a UI call on the runtime once it is up, with the loading and failure notices. */
25
+ export function createRuntimeOpener(bridge: RealtimeBridge): (openUi: (realtime: RealtimeApi) => void) => void {
26
+ return (openUi) => {
27
+ const loaded = bridge.current();
28
+ if (loaded !== null) {
29
+ openUi(loaded);
30
+ return;
31
+ }
32
+ // The menu already knows the runtime failed.
33
+ if (readRuntimeFailure() !== '') {
34
+ showRuntimeFailure();
35
+ return;
36
+ }
37
+ const slowTimer = setTimeout(() => {
38
+ showFallbackNotice('Friends are still loading…');
39
+ }, SLOW_NOTICE_MS);
40
+ let isSettled = false;
41
+ const onFailed = (): void => {
42
+ if (!isSettled) {
43
+ isSettled = true;
44
+ clearTimeout(slowTimer);
45
+ showRuntimeFailure();
46
+ }
47
+ };
48
+ if (typeof window !== 'undefined') {
49
+ window.addEventListener(REALTIME_FAILED_EVENT, onFailed, { once: true });
50
+ }
51
+ bridge.whenReady().then((realtime) => {
52
+ if (isSettled) {
53
+ return;
54
+ }
55
+ isSettled = true;
56
+ clearTimeout(slowTimer);
57
+ hideFallbackNotice();
58
+ openUi(realtime);
59
+ }, onFailed);
60
+ };
61
+ }
@@ -1,72 +1,77 @@
1
- /**
2
- * `hub.presence`, `hub.social` and `hub.notify`: thin forwarders to the
3
- * social runtime. Presence and quiet mode are "latest state" calls,
4
- * remembered and replayed when the runtime loads.
5
- */
6
- import { pageBridge } from './bridge';
7
- import { latestState } from './latest-state';
8
- import { createRuntimeOpener } from './runtime-opener';
9
- import type { HubNotifyApi, HubPresenceApi, HubSocialApi, SocialCounts } from './social-types';
10
-
11
- const openWithRuntime = createRuntimeOpener(pageBridge);
12
-
13
- /** Counts before the runtime is up. */
14
- const NO_COUNTS: SocialCounts = { online: 0, inGame: 0, friendsOnline: 0 };
15
-
16
- /** `hub.presence`: what you're doing, shown to friends. */
17
- export const presenceForwarder: HubPresenceApi = {
18
- set(presence) {
19
- latestState.presence = { ...presence };
20
- pageBridge.current()?.presence.set(latestState.presence);
21
- },
22
- clear() {
23
- latestState.presence = { kind: 'menu' };
24
- pageBridge.current()?.presence.clear();
25
- },
26
- };
27
-
28
- /** `hub.notify`: toast behaviour. */
29
- export const notifyForwarder: HubNotifyApi = {
30
- setQuiet(isQuiet) {
31
- latestState.isQuiet = isQuiet;
32
- pageBridge.current()?.notify.setQuiet(isQuiet);
33
- },
34
- };
35
-
36
- /** `hub.social`: friends and the hub's social dialogs. */
37
- export const socialForwarder: HubSocialApi = {
38
- openPlayer: (ref) => {
39
- openWithRuntime((realtime) => {
40
- realtime.social.openPlayer(ref);
41
- });
42
- },
43
- openFriends: () => {
44
- openWithRuntime((realtime) => {
45
- realtime.social.openFriends();
46
- });
47
- },
48
- openChat: (userId) => {
49
- openWithRuntime((realtime) => {
50
- realtime.social.openChat(userId);
51
- });
52
- },
53
- openInvite: (options) => {
54
- openWithRuntime((realtime) => {
55
- realtime.social.openInvite(options);
56
- });
57
- },
58
- openSuggest: () => {
59
- openWithRuntime((realtime) => {
60
- realtime.social.openSuggest();
61
- });
62
- },
63
- openNotify: (options) => {
64
- openWithRuntime((realtime) => {
65
- realtime.social.openNotify(options);
66
- });
67
- },
68
- friends: () => pageBridge.call((realtime) => realtime.social.friends()),
69
- counts: () => pageBridge.current()?.social.counts() ?? { ...NO_COUNTS },
70
- chatMode: (userIds) => pageBridge.current()?.social.chatMode(userIds) ?? 'off',
71
- on: (event, listener) => pageBridge.subscribe((realtime) => realtime.social.on(event, listener)),
72
- };
1
+ /**
2
+ * `hub.presence`, `hub.social` and `hub.notify`: thin forwarders to the
3
+ * social runtime. Presence and quiet mode are "latest state" calls,
4
+ * remembered and replayed when the runtime loads.
5
+ */
6
+ import { pageBridge } from './bridge';
7
+ import { latestState } from './latest-state';
8
+ import { createRuntimeOpener } from './runtime-opener';
9
+ import type { HubNotifyApi, HubPresenceApi, HubSocialApi, SocialCounts } from './social-types';
10
+
11
+ const openWithRuntime = createRuntimeOpener(pageBridge);
12
+
13
+ /** Counts before the runtime is up. */
14
+ const NO_COUNTS: SocialCounts = { online: 0, inGame: 0, friendsOnline: 0 };
15
+
16
+ /** `hub.presence`: what you're doing, shown to friends. */
17
+ export const presenceForwarder: HubPresenceApi = {
18
+ set(presence) {
19
+ latestState.presence = { ...presence };
20
+ pageBridge.current()?.presence.set(latestState.presence);
21
+ },
22
+ clear() {
23
+ latestState.presence = { kind: 'menu' };
24
+ pageBridge.current()?.presence.clear();
25
+ },
26
+ };
27
+
28
+ /** `hub.notify`: toast behaviour. */
29
+ export const notifyForwarder: HubNotifyApi = {
30
+ setQuiet(isQuiet) {
31
+ latestState.isQuiet = isQuiet;
32
+ pageBridge.current()?.notify.setQuiet(isQuiet);
33
+ },
34
+ };
35
+
36
+ /** `hub.social`: friends and the hub's social dialogs. */
37
+ export const socialForwarder: HubSocialApi = {
38
+ openPlayer: (ref) => {
39
+ openWithRuntime((realtime) => {
40
+ realtime.social.openPlayer(ref);
41
+ });
42
+ },
43
+ openFriends: () => {
44
+ openWithRuntime((realtime) => {
45
+ realtime.social.openFriends();
46
+ });
47
+ },
48
+ openChat: (userId) => {
49
+ openWithRuntime((realtime) => {
50
+ realtime.social.openChat(userId);
51
+ });
52
+ },
53
+ openInvite: (options) => {
54
+ openWithRuntime((realtime) => {
55
+ realtime.social.openInvite(options);
56
+ });
57
+ },
58
+ openSuggest: () => {
59
+ openWithRuntime((realtime) => {
60
+ realtime.social.openSuggest();
61
+ });
62
+ },
63
+ openNotify: (options) => {
64
+ openWithRuntime((realtime) => {
65
+ realtime.social.openNotify(options);
66
+ });
67
+ },
68
+ openPlayOnline: () => {
69
+ openWithRuntime((realtime) => {
70
+ realtime.social.openPlayOnline?.();
71
+ });
72
+ },
73
+ friends: () => pageBridge.call((realtime) => realtime.social.friends()),
74
+ counts: () => pageBridge.current()?.social.counts() ?? { ...NO_COUNTS },
75
+ chatMode: (userIds) => pageBridge.current()?.social.chatMode(userIds) ?? 'off',
76
+ on: (event, listener) => pageBridge.subscribe((realtime) => realtime.social.on(event, listener)),
77
+ };