@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.
- package/CHANGELOG.md +163 -0
- package/LICENSE +21 -21
- package/README.md +203 -139
- package/dist/auto.d.ts +8 -8
- package/dist/auto.js +3 -3
- package/dist/avatar-pixi.d.ts +165 -0
- package/dist/avatar-pixi.d.ts.map +1 -0
- package/dist/avatar-pixi.js +299 -0
- package/dist/avatar-pixi.js.map +1 -0
- package/dist/avatar-svg.d.ts +4 -0
- package/dist/avatar-svg.js +5 -0
- package/dist/avatar-three.d.ts +69 -0
- package/dist/avatar-three.d.ts.map +1 -0
- package/dist/avatar-three.js +49 -0
- package/dist/avatar-three.js.map +1 -0
- package/dist/avatar.d.ts +7 -0
- package/dist/avatar.js +500 -0
- package/dist/avatar.js.map +1 -0
- package/dist/daily.d.ts +2 -2
- package/dist/doctor/cli.mjs +119 -28
- package/dist/doctor/cli.mjs.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3 -3
- package/dist/input.d.ts +2 -2
- package/dist/input.js +2 -2
- package/dist/mp.d.ts +3 -3
- package/dist/race.d.ts +1 -1
- package/dist/rooms.d.ts +3 -3
- package/dist/rooms.js +1 -1
- package/dist/shared/animator.d.ts +104 -0
- package/dist/shared/animator.d.ts.map +1 -0
- package/dist/shared/art.js +1946 -0
- package/dist/shared/art.js.map +1 -0
- package/dist/shared/atlas.d.ts +138 -0
- package/dist/shared/atlas.d.ts.map +1 -0
- package/dist/shared/avatar.d.ts +123 -0
- package/dist/shared/avatar.d.ts.map +1 -0
- package/dist/shared/bridge.js +18 -4
- package/dist/shared/bridge.js.map +1 -1
- package/dist/shared/client-version.gen.js +1 -1
- package/dist/shared/daily.d.ts +145 -19
- package/dist/shared/daily.d.ts.map +1 -1
- package/dist/shared/daily.js +138 -25
- package/dist/shared/daily.js.map +1 -1
- package/dist/shared/defineProperty.js +39 -0
- package/dist/shared/describe.d.ts +32 -0
- package/dist/shared/describe.d.ts.map +1 -0
- package/dist/shared/describe.js +109 -0
- package/dist/shared/describe.js.map +1 -0
- package/dist/shared/globals.js +9 -42
- package/dist/shared/globals.js.map +1 -1
- package/dist/shared/index.d.ts +1122 -46
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/input.d.ts +74 -14
- package/dist/shared/input.d.ts.map +1 -1
- package/dist/shared/input.js +179 -25
- package/dist/shared/input.js.map +1 -1
- package/dist/shared/json-guards.js +48 -0
- package/dist/shared/json-guards.js.map +1 -0
- package/dist/shared/latest-state.js +1 -1
- package/dist/shared/live-canvas.js +62 -0
- package/dist/shared/live-canvas.js.map +1 -0
- package/dist/shared/mp.js +10 -2
- package/dist/shared/mp.js.map +1 -1
- package/dist/shared/multiplayer-types.d.ts +15 -2
- package/dist/shared/multiplayer-types.d.ts.map +1 -1
- package/dist/shared/overlays.js +800 -0
- package/dist/shared/overlays.js.map +1 -0
- package/dist/shared/page-stack.js +2 -1
- package/dist/shared/page-stack.js.map +1 -1
- package/dist/shared/pose-key.js +249 -0
- package/dist/shared/pose-key.js.map +1 -0
- package/dist/shared/race-types.d.ts +1 -1
- package/dist/shared/race.js +1 -1
- package/dist/shared/room-types.d.ts +28 -3
- package/dist/shared/room-types.d.ts.map +1 -1
- package/dist/shared/rooms.d.ts.map +1 -1
- package/dist/shared/rooms.js +23 -4
- package/dist/shared/rooms.js.map +1 -1
- package/dist/shared/runtime-opener.js +1 -1
- package/dist/shared/scene.d.ts +90 -0
- package/dist/shared/scene.d.ts.map +1 -0
- package/dist/shared/social.d.ts +90 -9
- package/dist/shared/social.d.ts.map +1 -1
- package/dist/shared/social.js +6 -1
- package/dist/shared/social.js.map +1 -1
- package/dist/shared/src.js +4563 -667
- package/dist/shared/src.js.map +1 -1
- package/dist/shared/svg-markup.js +2354 -0
- package/dist/shared/svg-markup.js.map +1 -0
- package/dist/shared/svg.js +27 -0
- package/dist/shared/svg.js.map +1 -0
- package/dist/shared/types.d.ts +174 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/wire-types.d.ts +6 -2
- package/dist/shared/wire-types.d.ts.map +1 -1
- package/dist/social.d.ts +5 -5
- package/dist/social.js +2 -2
- package/dist/turns-rules.d.ts +2 -0
- package/dist/turns-rules.js +1 -0
- package/dist/turns.d.ts +61 -0
- package/dist/turns.d.ts.map +1 -0
- package/dist/turns.js +183 -0
- package/dist/turns.js.map +1 -0
- package/package.json +125 -89
- package/src/auto.ts +39 -39
- package/src/avatar/adapters/pixi-avatar.ts +73 -0
- package/src/avatar/adapters/pixi-crowd.ts +329 -0
- package/src/avatar/adapters/pixi-types.ts +84 -0
- package/src/avatar/adapters/three.ts +126 -0
- package/src/avatar/animator/animator.ts +285 -0
- package/src/avatar/animator/reactions.ts +44 -0
- package/src/avatar/animator/schedule.ts +73 -0
- package/src/avatar/art/beard.ts +49 -0
- package/src/avatar/art/glasses.ts +67 -0
- package/src/avatar/art/hair.ts +254 -0
- package/src/avatar/art/headwear.ts +75 -0
- package/src/avatar/art/index.ts +21 -0
- package/src/avatar/art/shape.ts +87 -0
- package/src/avatar/art-store.ts +36 -0
- package/src/avatar/atlas.ts +246 -0
- package/src/avatar/catalog-view.ts +14 -0
- package/src/avatar/colour.ts +109 -0
- package/src/avatar/describe.ts +79 -0
- package/src/avatar/face.ts +187 -0
- package/src/avatar/geom.ts +173 -0
- package/src/avatar/layout.ts +47 -0
- package/src/avatar/look-like.ts +52 -0
- package/src/avatar/outputs/bitmap.ts +75 -0
- package/src/avatar/outputs/canvas-paint.ts +177 -0
- package/src/avatar/outputs/canvas.ts +62 -0
- package/src/avatar/outputs/custom-element.ts +140 -0
- package/src/avatar/outputs/element.ts +58 -0
- package/src/avatar/outputs/live-canvas.ts +57 -0
- package/src/avatar/outputs/overlay-svg.ts +22 -0
- package/src/avatar/outputs/pose-key.ts +30 -0
- package/src/avatar/outputs/svg-markup.ts +113 -0
- package/src/avatar/outputs/svg.ts +41 -0
- package/src/avatar/overlays.ts +128 -0
- package/src/avatar/render/body.ts +116 -0
- package/src/avatar/render/colours.ts +81 -0
- package/src/avatar/render/compose.ts +320 -0
- package/src/avatar/render/context.ts +117 -0
- package/src/avatar/render/eyes.ts +78 -0
- package/src/avatar/render/eyewear.ts +118 -0
- package/src/avatar/render/frame.ts +73 -0
- package/src/avatar/render/head.ts +81 -0
- package/src/avatar/render/mouth.ts +86 -0
- package/src/avatar/render/props.ts +91 -0
- package/src/avatar/render/types.ts +93 -0
- package/src/avatar/render/walker-parts.ts +121 -0
- package/src/avatar/render/walker.ts +199 -0
- package/src/avatar/scene.ts +119 -0
- package/src/avatar-pixi.ts +20 -0
- package/src/avatar-svg.ts +21 -0
- package/src/avatar-three.ts +18 -0
- package/src/avatar.ts +63 -0
- package/src/core/avatar/avatar-cache.ts +95 -0
- package/src/core/avatar/avatar-ref.ts +34 -0
- package/src/core/avatar/avatar-types.ts +43 -0
- package/src/core/avatar/hub-avatar.ts +308 -0
- package/src/core/connection.ts +408 -299
- package/src/core/errors.ts +160 -147
- package/src/core/game-data.ts +40 -38
- package/src/core/hub-economy.ts +216 -179
- package/src/core/hub-progress.ts +269 -205
- package/src/core/hub.ts +352 -131
- package/src/core/menu-control.ts +61 -61
- package/src/core/offline/cloud-saves.ts +62 -0
- package/src/core/offline/events.ts +50 -50
- package/src/core/offline/favorites.ts +21 -21
- package/src/core/offline/hub-kinds/avatar-kind.ts +42 -0
- package/src/core/offline/hub-kinds/daily-kind.ts +68 -0
- package/src/core/offline/hub-kinds/economy-kinds.ts +86 -0
- package/src/core/offline/hub-kinds/game-fields.ts +2 -0
- package/src/core/offline/hub-kinds/index.ts +52 -0
- package/src/core/offline/hub-kinds/legacy-ops.ts +37 -0
- package/src/core/offline/hub-kinds/progress-kinds.ts +165 -0
- package/src/core/offline/hub-kinds/resume-kind.ts +108 -0
- package/src/core/offline/hub-kinds/saved-kind.ts +93 -0
- package/src/core/offline/kind-adapters.ts +48 -0
- package/src/core/offline/local-inventory.ts +64 -0
- package/src/core/offline/local-store.ts +304 -261
- package/src/core/offline/offline-api.ts +65 -0
- package/src/core/offline/outbox-entry.ts +59 -0
- package/src/core/offline/outbox-kinds.ts +78 -0
- package/src/core/offline/outbox-merge.ts +39 -0
- package/src/core/offline/outbox-replayer.ts +141 -0
- package/src/core/offline/outbox-store.ts +85 -0
- package/src/core/offline/outbox.ts +232 -0
- package/src/core/offline/queue.ts +126 -171
- package/src/core/offline/replay-lease.ts +87 -0
- package/src/core/offline/replay-policy.ts +52 -0
- package/src/core/page-account.ts +121 -0
- package/src/core/page.ts +87 -74
- package/src/core/rest/endpoint-types.ts +22 -22
- package/src/core/rest/rest-client.ts +163 -152
- package/src/core/rest/routes.gen.ts +144 -88
- package/src/core/resume/hub-resume.ts +286 -0
- package/src/core/resume/local-resume.ts +130 -0
- package/src/core/resume/resume-list.ts +65 -0
- package/src/core/resume/resume-request.ts +52 -0
- package/src/core/resume/resume-types.ts +64 -0
- package/src/core/saved/daily-saves.ts +196 -0
- package/src/core/saved/daily-seed.ts +16 -0
- package/src/core/saved/hub-saved.ts +221 -0
- package/src/core/saved/local-saved.ts +122 -0
- package/src/core/saved/saved-list.ts +129 -0
- package/src/core/saved/saved-open.ts +104 -0
- package/src/core/saved/saved-order.ts +49 -0
- package/src/core/saved/saved-types.ts +24 -0
- package/src/core/storage/json-guards.ts +80 -74
- package/src/core/storage/key-value.ts +66 -66
- package/src/core/ticket.ts +43 -43
- package/src/core/turns/hub-turns.ts +161 -0
- package/src/core/turns/turn-game.ts +510 -0
- package/src/core/turns/your-turn-buzz.ts +69 -0
- package/src/core/types.ts +134 -134
- package/src/core/version/client-version.gen.ts +2 -2
- package/src/core/version/hub-version.ts +62 -62
- package/src/core/version/semver.ts +97 -97
- package/src/daily/daily-system.ts +276 -161
- package/src/daily/page-daily.ts +36 -36
- package/src/daily/types.ts +105 -67
- package/src/daily.ts +10 -10
- package/src/doctor/checks.ts +86 -76
- package/src/doctor/cli.ts +101 -106
- package/src/doctor/hub-version-fetch.ts +43 -0
- package/src/doctor/tsconfig.json +12 -12
- package/src/doctor/world-check.ts +61 -0
- package/src/globals.ts +115 -110
- package/src/index.ts +68 -50
- package/src/input/device-state.ts +146 -146
- package/src/input/edges.ts +55 -55
- package/src/input/focus-ring.ts +13 -0
- package/src/input/gamepad.ts +61 -61
- package/src/input/input-events.ts +32 -32
- package/src/input/input-registry.ts +171 -164
- package/src/input/input-system.ts +406 -393
- package/src/input/legacy-controls.ts +64 -64
- package/src/input/modality.ts +122 -0
- package/src/input/nav-state.ts +200 -191
- package/src/input/navigator.ts +182 -171
- package/src/input/on-screen-controls.ts +101 -101
- package/src/input/page-control.ts +43 -43
- package/src/input/page-input.ts +193 -193
- package/src/input/player-remaps.ts +77 -77
- package/src/input/source-values.ts +116 -116
- package/src/input/spatial.ts +47 -47
- package/src/input/types.ts +331 -331
- package/src/input/zero-config.ts +164 -177
- package/src/input.ts +26 -22
- package/src/mp.ts +10 -10
- package/src/overlay/controls-panel.ts +163 -163
- package/src/overlay/dom.ts +46 -46
- package/src/overlay/layout-clusters.ts +162 -162
- package/src/overlay/layout-geometry.ts +136 -136
- package/src/overlay/layout.ts +203 -203
- package/src/overlay/listener-scope.ts +23 -23
- package/src/overlay/on-screen-keyboard.ts +137 -137
- package/src/overlay/overlay.ts +234 -208
- package/src/overlay/styles.ts +13 -12
- package/src/overlay/virtual-controls.ts +262 -262
- package/src/race.ts +10 -10
- package/src/realtime/bridge.ts +126 -121
- package/src/realtime/cross-bundle.ts +29 -29
- package/src/realtime/fallback-notice.ts +48 -48
- package/src/realtime/hub-link.ts +65 -65
- package/src/realtime/latest-state.ts +33 -33
- package/src/realtime/multiplayer-forwarder.ts +40 -28
- package/src/realtime/multiplayer-types.ts +66 -52
- package/src/realtime/page-runtime.ts +60 -60
- package/src/realtime/race-forwarder.ts +59 -59
- package/src/realtime/race-types.ts +132 -132
- package/src/realtime/room-types.ts +151 -125
- package/src/realtime/rooms-forwarder.ts +104 -79
- package/src/realtime/runtime-api.ts +74 -49
- package/src/realtime/runtime-opener.ts +61 -61
- package/src/realtime/social-forwarder.ts +77 -72
- package/src/realtime/social-types.ts +86 -78
- package/src/realtime/types.ts +54 -53
- package/src/realtime/wire-types.ts +79 -72
- package/src/realtime/world-types.ts +20 -0
- package/src/rooms.ts +10 -10
- package/src/social.ts +19 -18
- package/src/turns-kit/check-rules.ts +281 -0
- package/src/turns-rules.ts +12 -0
- package/src/turns.ts +14 -0
- package/src/types.ts +13 -13
- package/src/uistack/page-stack.ts +45 -45
- package/src/uistack/play-suspension.ts +82 -82
- package/src/uistack/types.ts +76 -76
- package/src/uistack/ui-stack.ts +144 -144
package/src/core/hub-economy.ts
CHANGED
|
@@ -1,179 +1,216 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analytics, the catalog, favorites, records, inventory and trades. Analytics
|
|
3
|
-
* calls are fire-and-forget and never throw (telemetry must not break a game);
|
|
4
|
-
* offline they are buffered and sent when the hub is back. Method names are
|
|
5
|
-
* the v1 client's, unchanged.
|
|
6
|
-
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return this.withFallback(
|
|
105
|
-
|
|
106
|
-
(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Analytics, the catalog, favorites, records, inventory and trades. Analytics
|
|
3
|
+
* calls are fire-and-forget and never throw (telemetry must not break a game);
|
|
4
|
+
* offline they are buffered and sent when the hub is back. Method names are
|
|
5
|
+
* the v1 client's, unchanged.
|
|
6
|
+
*/
|
|
7
|
+
import type { Json } from '@diffenderfer-games/hub-contract';
|
|
8
|
+
import { HubProgress } from './hub-progress';
|
|
9
|
+
import { readLocalFavorites, writeLocalFavorites } from './offline/favorites';
|
|
10
|
+
import { ECONOMY_KINDS, type ExchangePayload } from './offline/hub-kinds';
|
|
11
|
+
import { readInventory, withGoods, withoutGoods, type GoodsPayload } from './offline/local-inventory';
|
|
12
|
+
import { HubError } from './errors';
|
|
13
|
+
import { isRecord, stringsIn } from './storage/json-guards';
|
|
14
|
+
import type { EndpointFields, EndpointReply } from './rest/endpoint-types';
|
|
15
|
+
import type { GameAnalytics, GameInfo, GlobalAnalytics, InventorySnapshot } from './types';
|
|
16
|
+
|
|
17
|
+
/** Goods to grant: item key to quantity, and coins. */
|
|
18
|
+
export type GrantBundle = Omit<EndpointFields<'grantInventoryItems'>, 'slug'>;
|
|
19
|
+
|
|
20
|
+
/** Goods taken or given in an exchange or a trade. */
|
|
21
|
+
export type TradeBundle = EndpointFields<'exchangeInventoryItems'>['take'];
|
|
22
|
+
|
|
23
|
+
/** A trade offer's id, as a number or its text. */
|
|
24
|
+
export type TradeId = number | string;
|
|
25
|
+
|
|
26
|
+
/** Extras for creating or countering a trade. */
|
|
27
|
+
export interface TradeNoteOptions {
|
|
28
|
+
/** A short note to the other player (moderated). */
|
|
29
|
+
readonly note?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Goods as queued: `null` parts left out. */
|
|
33
|
+
function goodsOf(bundle: TradeBundle | GrantBundle | undefined): GoodsPayload {
|
|
34
|
+
const items = bundle?.items ?? undefined;
|
|
35
|
+
const coins = bundle?.coins ?? undefined;
|
|
36
|
+
return { ...(items === undefined ? {} : { items }), ...(coins === undefined ? {} : { coins }) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The holdings in a grant or exchange reply, or `undefined` when it doesn't read as one. */
|
|
40
|
+
function inventoryReply(result: Json | undefined): InventorySnapshot | undefined {
|
|
41
|
+
return readInventory(result) ?? undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Which trades `listTrades` lists. */
|
|
45
|
+
export type TradeListOptions = Omit<EndpointFields<'listMyTrades'>, 'slug'>;
|
|
46
|
+
|
|
47
|
+
/** Analytics, catalog, favorites, records, inventory and trades. */
|
|
48
|
+
export class HubEconomy extends HubProgress {
|
|
49
|
+
/** Record a "play". Call once when the game starts. */
|
|
50
|
+
recordPlay(): Promise<void> {
|
|
51
|
+
return this.event('play');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Record a "heartbeat". Call periodically while the game is active. */
|
|
55
|
+
heartbeat(): Promise<void> {
|
|
56
|
+
return this.event('heartbeat');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Record a custom event. Never throws; offline, events are buffered and sent on reconnect. */
|
|
60
|
+
async event(type: string): Promise<void> {
|
|
61
|
+
if (this.slug === null) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (this.isOffline) {
|
|
65
|
+
this.store.bufferEvent(type);
|
|
66
|
+
this.notifyStatus();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await this.rest.call('recordGameEvent', { ...this.gameFields(), type });
|
|
71
|
+
} catch (failure) {
|
|
72
|
+
if (failure instanceof HubError && failure.isOffline) {
|
|
73
|
+
this.setReachable(false);
|
|
74
|
+
this.store.bufferEvent(type);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** This game's play counts. */
|
|
80
|
+
analytics(): Promise<GameAnalytics> {
|
|
81
|
+
return this.withFallback(() => this.rest.call('getGameAnalytics', this.gameFields()), () => ({ slug: this.slug ?? 'app', totalPlays: 0, uniquePlayers: 0, activeNow: 0 }));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Catalog-wide play counts with a per-game breakdown. */
|
|
85
|
+
globalAnalytics(): Promise<GlobalAnalytics> {
|
|
86
|
+
return this.withFallback(() => this.rest.call('getSiteAnalytics'), () => ({ global: { totalPlays: 0, uniquePlayers: 0, activeNow: 0 }, games: [] }));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The listed games, for "jump to another game" navigation. */
|
|
90
|
+
listGames(): Promise<{ games: GameInfo[] }> {
|
|
91
|
+
return this.withFallback(() => this.rest.call('listGames'), () => ({ games: [] }));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Every board of every game, each with its top `top` entries (default 5). */
|
|
95
|
+
allLeaderboards(top = 5): Promise<EndpointReply<'listTopOfEveryLeaderboard'>> {
|
|
96
|
+
return this.withFallback(() => this.rest.call('listTopOfEveryLeaderboard', { top }), () => ({ games: [] }));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The starred games (slugs), shown first in the menu's game list. Kept per
|
|
101
|
+
* account by the hub and mirrored to `localStorage`, so it shows offline.
|
|
102
|
+
*/
|
|
103
|
+
favorites(): Promise<{ games: string[] }> {
|
|
104
|
+
return this.withFallback(async () => {
|
|
105
|
+
const reply = await this.rest.call('listFavoriteGames');
|
|
106
|
+
writeLocalFavorites(reply.games);
|
|
107
|
+
return reply;
|
|
108
|
+
}, () => ({ games: readLocalFavorites() }));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Star (`isOn = true`) or unstar a game. Offline, the local list changes at once and the hub's when it is back. */
|
|
112
|
+
setFavorite(slug: string, isOn: boolean): Promise<{ games: string[] }> {
|
|
113
|
+
return this.mutateOffline(ECONOMY_KINDS.favorite, { slug, on: isOn }, () => {
|
|
114
|
+
const others = readLocalFavorites().filter((starred) => starred !== slug);
|
|
115
|
+
const games = isOn ? [...others, slug] : others;
|
|
116
|
+
writeLocalFavorites(games);
|
|
117
|
+
return { games };
|
|
118
|
+
}, (result) => (isRecord(result) ? { games: stringsIn(result.games) } : undefined));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** The player's leaderboard standings, grouped by game (every game, or one via `forSlug`). Empty offline or signed out. */
|
|
122
|
+
myRecords(forSlug?: string): Promise<EndpointReply<'listMyLeaderboardRecords'>> {
|
|
123
|
+
return this.withFallback(
|
|
124
|
+
() => this.rest.call('listMyLeaderboardRecords', forSlug === undefined || forSlug === '' ? {} : { game: forSlug }),
|
|
125
|
+
() => ({ games: [] }),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** This game's inventory and coin balance for the current user. */
|
|
130
|
+
getInventory(): Promise<InventorySnapshot> {
|
|
131
|
+
return this.withFallback(async () => this.store.setInventory(await this.rest.call('getMyInventory', this.gameFields())), () => this.store.getInventory());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Another player's available holdings (to compose a trade). */
|
|
135
|
+
playerInventory(userId: number): Promise<InventorySnapshot> {
|
|
136
|
+
return this.withFallback(() => this.rest.call('getPlayerInventory', { ...this.gameFields(), userId }), () => ({ items: [], coins: 0 }));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Add goods to the player (gameplay rewards); answers the new holdings.
|
|
141
|
+
* Offline, the cached holdings change at once and the grant replays later.
|
|
142
|
+
*/
|
|
143
|
+
grant(bundle: GrantBundle): Promise<EndpointReply<'grantInventoryItems'>> {
|
|
144
|
+
const goods = goodsOf(bundle);
|
|
145
|
+
return this.mutateOffline(ECONOMY_KINDS.grant, goods, () => this.store.setInventory(withGoods(this.store.getInventory(), goods)), inventoryReply);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* An atomic swap with the house: remove `take`, add `give`. Offline it is
|
|
150
|
+
* checked against the cached holdings (409 when they can't cover `take`),
|
|
151
|
+
* applied to them at once, and replayed later; the hub has the last word.
|
|
152
|
+
*/
|
|
153
|
+
exchange(take?: TradeBundle, give?: TradeBundle): Promise<EndpointReply<'exchangeInventoryItems'>> {
|
|
154
|
+
const payload: ExchangePayload = { ...(take === undefined ? {} : { take: goodsOf(take) }), ...(give === undefined ? {} : { give: goodsOf(give) }) };
|
|
155
|
+
return this.mutateOffline(ECONOMY_KINDS.exchange, payload, () => {
|
|
156
|
+
const remaining = withoutGoods(this.store.getInventory(), payload.take ?? {});
|
|
157
|
+
if (typeof remaining === 'string') {
|
|
158
|
+
if (this.isOffline) {
|
|
159
|
+
throw new HubError(remaining, 409);
|
|
160
|
+
}
|
|
161
|
+
return this.store.getInventory();
|
|
162
|
+
}
|
|
163
|
+
return this.store.setInventory(withGoods(remaining, payload.give ?? {}));
|
|
164
|
+
}, inventoryReply);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Sell items to the house for coins. */
|
|
168
|
+
sell(items: Record<string, number>, coins: number): Promise<EndpointReply<'exchangeInventoryItems'>> {
|
|
169
|
+
return this.exchange({ items }, { coins });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Buy items from the house with coins. */
|
|
173
|
+
buy(coins: number, items: Record<string, number>): Promise<EndpointReply<'exchangeInventoryItems'>> {
|
|
174
|
+
return this.exchange({ coins }, { items });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Wipe the player's progress in this game (saves, stats, inventory and coins). */
|
|
178
|
+
resetGame(): Promise<{ ok: true }> {
|
|
179
|
+
return this.rest.call('resetMyGameProgress', this.gameFields());
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Players this player can trade with in this game. */
|
|
183
|
+
listPlayers(): Promise<EndpointReply<'listTradePartners'>> {
|
|
184
|
+
return this.withFallback(() => this.rest.call('listTradePartners', this.gameFields()), () => ({ players: [] }));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The player's trades (`box`: `incoming` or `outgoing`; `status` filters). */
|
|
188
|
+
listTrades(opts: TradeListOptions = {}): Promise<EndpointReply<'listMyTrades'>> {
|
|
189
|
+
return this.withFallback(() => this.rest.call('listMyTrades', { ...this.gameFields(), ...opts }), () => ({ offers: [] }));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Offer a trade to another player (by id or name). */
|
|
193
|
+
createTrade(toUser: number | string, give?: TradeBundle, want?: TradeBundle, opts: TradeNoteOptions = {}): Promise<EndpointReply<'createTrade'>> {
|
|
194
|
+
return this.rest.call('createTrade', { ...this.gameFields(), toUser: String(toUser), ...(give === undefined ? {} : { give }), ...(want === undefined ? {} : { want }), ...opts });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Accept a trade offered to the player. */
|
|
198
|
+
acceptTrade(id: TradeId): Promise<EndpointReply<'acceptTrade'>> {
|
|
199
|
+
return this.rest.call('acceptTrade', { ...this.gameFields(), id: Number(id) });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Decline a trade offered to the player. */
|
|
203
|
+
declineTrade(id: TradeId): Promise<EndpointReply<'declineTrade'>> {
|
|
204
|
+
return this.rest.call('declineTrade', { ...this.gameFields(), id: Number(id) });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Cancel a trade the player offered. */
|
|
208
|
+
cancelTrade(id: TradeId): Promise<EndpointReply<'cancelTrade'>> {
|
|
209
|
+
return this.rest.call('cancelTrade', { ...this.gameFields(), id: Number(id) });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Answer a trade with a counter-offer. */
|
|
213
|
+
counterTrade(id: TradeId, give?: TradeBundle, want?: TradeBundle, opts: TradeNoteOptions = {}): Promise<EndpointReply<'counterTradeOffer'>> {
|
|
214
|
+
return this.rest.call('counterTradeOffer', { ...this.gameFields(), id: Number(id), ...(give === undefined ? {} : { give }), ...(want === undefined ? {} : { want }), ...opts });
|
|
215
|
+
}
|
|
216
|
+
}
|