@rydr/game-sdk 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/dist/client/HardwareStore.d.ts +40 -0
- package/dist/client/HardwareStore.d.ts.map +1 -0
- package/dist/client/HardwareStore.js +36 -0
- package/dist/client/HardwareStore.js.map +1 -0
- package/dist/client/PlatformClient.d.ts +182 -0
- package/dist/client/PlatformClient.d.ts.map +1 -0
- package/dist/client/PlatformClient.js +374 -0
- package/dist/client/PlatformClient.js.map +1 -0
- package/dist/client/Room.d.ts +127 -0
- package/dist/client/Room.d.ts.map +1 -0
- package/dist/client/Room.js +235 -0
- package/dist/client/Room.js.map +1 -0
- package/dist/client/adminContent.d.ts +65 -0
- package/dist/client/adminContent.d.ts.map +1 -0
- package/dist/client/adminContent.js +75 -0
- package/dist/client/adminContent.js.map +1 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/replayCodec.d.ts +27 -0
- package/dist/client/replayCodec.d.ts.map +1 -0
- package/dist/client/replayCodec.js +74 -0
- package/dist/client/replayCodec.js.map +1 -0
- package/dist/host/PlatformHost.d.ts +169 -0
- package/dist/host/PlatformHost.d.ts.map +1 -0
- package/dist/host/PlatformHost.js +248 -0
- package/dist/host/PlatformHost.js.map +1 -0
- package/dist/host/index.d.ts +3 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +3 -0
- package/dist/host/index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/boards.d.ts +61 -0
- package/dist/protocol/boards.d.ts.map +1 -0
- package/dist/protocol/boards.js +53 -0
- package/dist/protocol/boards.js.map +1 -0
- package/dist/protocol/buttons.d.ts +13 -0
- package/dist/protocol/buttons.d.ts.map +1 -0
- package/dist/protocol/buttons.js +2 -0
- package/dist/protocol/buttons.js.map +1 -0
- package/dist/protocol/capabilities.d.ts +23 -0
- package/dist/protocol/capabilities.d.ts.map +1 -0
- package/dist/protocol/capabilities.js +10 -0
- package/dist/protocol/capabilities.js.map +1 -0
- package/dist/protocol/gamedata.d.ts +23 -0
- package/dist/protocol/gamedata.d.ts.map +1 -0
- package/dist/protocol/gamedata.js +2 -0
- package/dist/protocol/gamedata.js.map +1 -0
- package/dist/protocol/guards.d.ts +15 -0
- package/dist/protocol/guards.d.ts.map +1 -0
- package/dist/protocol/guards.js +45 -0
- package/dist/protocol/guards.js.map +1 -0
- package/dist/protocol/identity.d.ts +21 -0
- package/dist/protocol/identity.d.ts.map +1 -0
- package/dist/protocol/identity.js +2 -0
- package/dist/protocol/identity.js.map +1 -0
- package/dist/protocol/index.d.ts +12 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +12 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/messages.d.ts +407 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/protocol/messages.js +2 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/protocol/replays.d.ts +67 -0
- package/dist/protocol/replays.d.ts.map +1 -0
- package/dist/protocol/replays.js +14 -0
- package/dist/protocol/replays.js.map +1 -0
- package/dist/protocol/room.d.ts +55 -0
- package/dist/protocol/room.d.ts.map +1 -0
- package/dist/protocol/room.js +13 -0
- package/dist/protocol/room.js.map +1 -0
- package/dist/protocol/version.d.ts +12 -0
- package/dist/protocol/version.d.ts.map +1 -0
- package/dist/protocol/version.js +12 -0
- package/dist/protocol/version.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform-side counterpart to {@link connectToPlatform}.
|
|
3
|
+
*
|
|
4
|
+
* `createPlatformHost()` binds one embedded game iframe to the shell: it answers
|
|
5
|
+
* the game's `hello` with a `welcome` (granting capabilities and a scoped,
|
|
6
|
+
* PII-free identity), streams hardware from a pluggable {@link HardwareSource},
|
|
7
|
+
* relays trainer-control commands back to it, and surfaces lifecycle/route/exit
|
|
8
|
+
* events as callbacks. The shell supplies the real hardware + identity; this
|
|
9
|
+
* host owns none of it — it only mediates the protocol.
|
|
10
|
+
*/
|
|
11
|
+
import type { Capability } from "../protocol/capabilities";
|
|
12
|
+
import type { ScopedIdentity } from "../protocol/identity";
|
|
13
|
+
import type { ButtonName, ButtonEdge } from "../protocol/buttons";
|
|
14
|
+
import type { BoardDefinition, LeaderboardPage, SubmitScoreResult } from "../protocol/boards";
|
|
15
|
+
import type { GameDataScope, GameDoc } from "../protocol/gamedata";
|
|
16
|
+
import type { ReplayMeta } from "../protocol/replays";
|
|
17
|
+
import type { RoomMember, RoomTelemetry, RoomEvent } from "../protocol/room";
|
|
18
|
+
/** A generic game-data operation the host relays to the shell's data service. */
|
|
19
|
+
export interface GameDataOp {
|
|
20
|
+
kind: "get" | "list" | "save" | "delete";
|
|
21
|
+
scope: GameDataScope;
|
|
22
|
+
collection: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
data?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** Result the shell returns for a {@link GameDataOp}. `get`→doc, `list`→docs, `save`/`delete`→ok. */
|
|
27
|
+
export interface GameDataResult {
|
|
28
|
+
doc?: GameDoc | null;
|
|
29
|
+
docs?: GameDoc[];
|
|
30
|
+
ok?: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* How the shell pushes room events back to the embedded game. The host gives one of these to the
|
|
35
|
+
* shell when the game joins a room; the shell calls it as its room socket produces events. The
|
|
36
|
+
* host turns each call into the matching `rydr/room.*` frame to the iframe.
|
|
37
|
+
*/
|
|
38
|
+
export interface RoomEventSink {
|
|
39
|
+
opened(): void;
|
|
40
|
+
closed(): void;
|
|
41
|
+
presence(members: RoomMember[]): void;
|
|
42
|
+
state(state: Record<string, unknown>): void;
|
|
43
|
+
message(from: string, data: unknown): void;
|
|
44
|
+
/** A trusted, shell-stamped telemetry reading for one room member. */
|
|
45
|
+
telemetry(reading: RoomTelemetry): void;
|
|
46
|
+
/** A server-stamped orchestration event broadcast to the room. */
|
|
47
|
+
event(e: RoomEvent): void;
|
|
48
|
+
}
|
|
49
|
+
/** What the shell returns to the host to drive a joined room on the game's behalf. */
|
|
50
|
+
export interface RoomController {
|
|
51
|
+
send(data: unknown): void;
|
|
52
|
+
setState(patch: Record<string, unknown>): void;
|
|
53
|
+
scheduleEvent(name: string, payload?: unknown, at?: number): void;
|
|
54
|
+
leave(): void;
|
|
55
|
+
}
|
|
56
|
+
/** A point-in-time hardware reading pushed to connected games. Omitted fields are unchanged. */
|
|
57
|
+
export interface HardwareSample {
|
|
58
|
+
power?: number;
|
|
59
|
+
cadence?: number;
|
|
60
|
+
heartRate?: number;
|
|
61
|
+
speed?: number;
|
|
62
|
+
/** ms timestamp from the platform clock. */
|
|
63
|
+
t: number;
|
|
64
|
+
}
|
|
65
|
+
/** The shell's hardware, adapted to what the host needs. The shell implements this over its HardwareManager. */
|
|
66
|
+
export interface HardwareSource {
|
|
67
|
+
/** Subscribe to live readings. Returns an unsubscribe fn. */
|
|
68
|
+
subscribe(listener: (sample: HardwareSample) => void): () => void;
|
|
69
|
+
/** Current trainer connection / ERG status. */
|
|
70
|
+
getStatus(): {
|
|
71
|
+
connected: boolean;
|
|
72
|
+
ergSupported: boolean;
|
|
73
|
+
};
|
|
74
|
+
/** Apply grade-based resistance. */
|
|
75
|
+
setSimulation(gradePercent: number): void;
|
|
76
|
+
/** Apply power-based (ERG) target. */
|
|
77
|
+
setTargetPower(watts: number): void;
|
|
78
|
+
/** Toggle ERG mode. */
|
|
79
|
+
setErgMode(enabled: boolean): void;
|
|
80
|
+
}
|
|
81
|
+
export interface PlatformHostOptions {
|
|
82
|
+
/** The embedded game iframe. */
|
|
83
|
+
iframe: HTMLIFrameElement;
|
|
84
|
+
/** Origin the game is served from. Messages from any other origin are ignored. Default `"*"` (dev only). */
|
|
85
|
+
gameOrigin?: string;
|
|
86
|
+
/** The shell's hardware. */
|
|
87
|
+
hardware: HardwareSource;
|
|
88
|
+
/** Produce the scoped identity granted to this game. */
|
|
89
|
+
identityFor(gameId: string): ScopedIdentity;
|
|
90
|
+
/** Decide which requested capabilities to grant. Default: grant all requested. */
|
|
91
|
+
grantFor?(gameId: string, requested: Capability[]): Capability[];
|
|
92
|
+
/** Initial deep-link path to hand the game. */
|
|
93
|
+
initialPath?: string;
|
|
94
|
+
/** The game's declared leaderboard boards (from its manifest), handed over in `welcome`. */
|
|
95
|
+
boards?: BoardDefinition[];
|
|
96
|
+
/** The run id this session is recorded under, handed over in `welcome`. */
|
|
97
|
+
runId?: string;
|
|
98
|
+
/** The `rydr` backend host, handed over in `welcome` so the game can open realtime rooms. */
|
|
99
|
+
dataHost?: string;
|
|
100
|
+
onReady?(): void;
|
|
101
|
+
onLoadProgress?(progress: number): void;
|
|
102
|
+
onExitRequest?(): void;
|
|
103
|
+
onRouteChanged?(path: string): void;
|
|
104
|
+
/** The game asked to show/hide shell chrome (navbar). */
|
|
105
|
+
onChromeRequest?(visible: boolean): void;
|
|
106
|
+
/** The game asked to show/hide the trainerless power bar. */
|
|
107
|
+
onPowerBarRequest?(visible: boolean): void;
|
|
108
|
+
onRequestHardwareModal?(): void;
|
|
109
|
+
onError?(message: string): void;
|
|
110
|
+
/** The game submitted a score; perform the authenticated write and return the rank/PB. */
|
|
111
|
+
onSubmitScore?(boardId: string, value: number, key?: string): Promise<SubmitScoreResult>;
|
|
112
|
+
/** The game requested a leaderboard page; fetch and return it. */
|
|
113
|
+
onGetLeaderboard?(boardId: string, opts: {
|
|
114
|
+
key?: string;
|
|
115
|
+
limit?: number;
|
|
116
|
+
}): Promise<LeaderboardPage>;
|
|
117
|
+
/** The game saved an opaque run breakdown; persist it against the run id. */
|
|
118
|
+
onSaveRun?(breakdown: unknown): void;
|
|
119
|
+
/** The game read/wrote the generic game-data store; perform it and return the result. */
|
|
120
|
+
onGameData?(op: GameDataOp): Promise<GameDataResult>;
|
|
121
|
+
/** The game requested a presigned upload URL for a binary asset; mint and return it. */
|
|
122
|
+
onGetUploadUrl?(req: {
|
|
123
|
+
collection: string;
|
|
124
|
+
filename: string;
|
|
125
|
+
contentType?: string;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
uploadUrl?: string;
|
|
128
|
+
url?: string;
|
|
129
|
+
error?: string;
|
|
130
|
+
}>;
|
|
131
|
+
/** The game saved a replay/ghost blob (base64) + derived {@link ReplayMeta} keyed by `runId`;
|
|
132
|
+
* persist both. */
|
|
133
|
+
onSaveReplay?(runId: string, blob: string, meta: ReplayMeta): Promise<{
|
|
134
|
+
ok?: boolean;
|
|
135
|
+
error?: string;
|
|
136
|
+
}>;
|
|
137
|
+
/** The game requested a stored replay by `runId`; fetch and return its blob + meta (`null` if absent). */
|
|
138
|
+
onGetReplay?(runId: string): Promise<{
|
|
139
|
+
blob?: string | null;
|
|
140
|
+
meta?: ReplayMeta | null;
|
|
141
|
+
error?: string;
|
|
142
|
+
}>;
|
|
143
|
+
/** The game requested a stored run breakdown by `runId`; fetch and return it. */
|
|
144
|
+
onGetRun?(runId: string): Promise<{
|
|
145
|
+
breakdown?: unknown;
|
|
146
|
+
error?: string;
|
|
147
|
+
}>;
|
|
148
|
+
/**
|
|
149
|
+
* The game joined a realtime room. The shell opens/owns the socket, injects trusted telemetry,
|
|
150
|
+
* and pushes events back through `sink`; it returns a {@link RoomController} the host uses to
|
|
151
|
+
* relay the game's `send`/`setState`/`leave`. Return `undefined` if the shell can't host rooms.
|
|
152
|
+
*/
|
|
153
|
+
onRoomJoin?(roomId: string, sink: RoomEventSink): RoomController | undefined;
|
|
154
|
+
}
|
|
155
|
+
export interface PlatformHost {
|
|
156
|
+
/** Tell the game to pause (e.g. a shell modal opened). */
|
|
157
|
+
pause(): void;
|
|
158
|
+
/** Tell the game to resume. */
|
|
159
|
+
resume(): void;
|
|
160
|
+
/** Push a controller button to the game (if it was granted `buttons`). */
|
|
161
|
+
sendButton(name: ButtonName, edge: ButtonEdge): void;
|
|
162
|
+
/** Push an updated scoped identity (e.g. after a profile edit). */
|
|
163
|
+
updateIdentity(identity: ScopedIdentity): void;
|
|
164
|
+
/** Tear down listeners and stop streaming. */
|
|
165
|
+
dispose(): void;
|
|
166
|
+
}
|
|
167
|
+
/** Bind a game iframe to the shell over the wire protocol. */
|
|
168
|
+
export declare function createPlatformHost(options: PlatformHostOptions): PlatformHost;
|
|
169
|
+
//# sourceMappingURL=PlatformHost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlatformHost.d.ts","sourceRoot":"","sources":["../../src/host/PlatformHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { RYDR_PROTOCOL_VERSION } from "../protocol/version";
|
|
2
|
+
import { isGameToPlatformMessage } from "../protocol/guards";
|
|
3
|
+
/** Bind a game iframe to the shell over the wire protocol. */
|
|
4
|
+
export function createPlatformHost(options) {
|
|
5
|
+
const { iframe, gameOrigin = "*", hardware, identityFor } = options;
|
|
6
|
+
const grantFor = options.grantFor ?? ((_id, requested) => requested);
|
|
7
|
+
let granted = [];
|
|
8
|
+
let unsubscribeHardware = null;
|
|
9
|
+
/** Rooms the game has joined this session, keyed by roomId. */
|
|
10
|
+
const roomControllers = new Map();
|
|
11
|
+
const post = (message) => {
|
|
12
|
+
iframe.contentWindow?.postMessage(message, gameOrigin);
|
|
13
|
+
};
|
|
14
|
+
const can = (capability) => granted.includes(capability);
|
|
15
|
+
const onHardware = (s) => {
|
|
16
|
+
if (s.power !== undefined && can("power"))
|
|
17
|
+
post({ rydr: true, type: "rydr/hw.power", watts: s.power, t: s.t });
|
|
18
|
+
if (s.cadence !== undefined && can("cadence"))
|
|
19
|
+
post({ rydr: true, type: "rydr/hw.cadence", rpm: s.cadence, t: s.t });
|
|
20
|
+
if (s.heartRate !== undefined && can("heartRate"))
|
|
21
|
+
post({ rydr: true, type: "rydr/hw.heartRate", bpm: s.heartRate, t: s.t });
|
|
22
|
+
if (s.speed !== undefined && can("speed"))
|
|
23
|
+
post({ rydr: true, type: "rydr/hw.speed", mps: s.speed, t: s.t });
|
|
24
|
+
};
|
|
25
|
+
const onMessage = (event) => {
|
|
26
|
+
if (event.source !== iframe.contentWindow)
|
|
27
|
+
return;
|
|
28
|
+
if (gameOrigin !== "*" && event.origin !== gameOrigin)
|
|
29
|
+
return;
|
|
30
|
+
const msg = event.data;
|
|
31
|
+
if (!isGameToPlatformMessage(msg))
|
|
32
|
+
return;
|
|
33
|
+
switch (msg.type) {
|
|
34
|
+
case "rydr/hello": {
|
|
35
|
+
granted = grantFor(msg.gameId, msg.capabilities);
|
|
36
|
+
post({
|
|
37
|
+
rydr: true,
|
|
38
|
+
type: "rydr/welcome",
|
|
39
|
+
protocolVersion: RYDR_PROTOCOL_VERSION,
|
|
40
|
+
grantedCapabilities: granted,
|
|
41
|
+
identity: identityFor(msg.gameId),
|
|
42
|
+
initialPath: options.initialPath,
|
|
43
|
+
boards: options.boards,
|
|
44
|
+
runId: options.runId,
|
|
45
|
+
dataHost: options.dataHost,
|
|
46
|
+
});
|
|
47
|
+
const status = hardware.getStatus();
|
|
48
|
+
post({ rydr: true, type: "rydr/trainer.status", connected: status.connected, ergSupported: status.ergSupported });
|
|
49
|
+
unsubscribeHardware?.();
|
|
50
|
+
unsubscribeHardware = hardware.subscribe(onHardware);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "rydr/ready":
|
|
54
|
+
options.onReady?.();
|
|
55
|
+
break;
|
|
56
|
+
case "rydr/loadProgress":
|
|
57
|
+
options.onLoadProgress?.(msg.progress);
|
|
58
|
+
break;
|
|
59
|
+
case "rydr/trainer.setSimulation":
|
|
60
|
+
hardware.setSimulation(msg.gradePercent);
|
|
61
|
+
break;
|
|
62
|
+
case "rydr/trainer.setTargetPower":
|
|
63
|
+
hardware.setTargetPower(msg.watts);
|
|
64
|
+
break;
|
|
65
|
+
case "rydr/trainer.setErgMode":
|
|
66
|
+
hardware.setErgMode(msg.enabled);
|
|
67
|
+
break;
|
|
68
|
+
case "rydr/route.changed":
|
|
69
|
+
options.onRouteChanged?.(msg.path);
|
|
70
|
+
break;
|
|
71
|
+
case "rydr/ui.setChrome":
|
|
72
|
+
options.onChromeRequest?.(msg.visible);
|
|
73
|
+
break;
|
|
74
|
+
case "rydr/ui.setPowerBar":
|
|
75
|
+
options.onPowerBarRequest?.(msg.visible);
|
|
76
|
+
break;
|
|
77
|
+
case "rydr/exitRequest":
|
|
78
|
+
options.onExitRequest?.();
|
|
79
|
+
break;
|
|
80
|
+
case "rydr/ui.requestHardwareModal":
|
|
81
|
+
options.onRequestHardwareModal?.();
|
|
82
|
+
break;
|
|
83
|
+
case "rydr/pong":
|
|
84
|
+
// liveness reply — no-op for now
|
|
85
|
+
break;
|
|
86
|
+
case "rydr/error":
|
|
87
|
+
options.onError?.(msg.message);
|
|
88
|
+
break;
|
|
89
|
+
case "rydr/leaderboard.submit": {
|
|
90
|
+
const { nonce } = msg;
|
|
91
|
+
const handler = options.onSubmitScore ??
|
|
92
|
+
(async () => ({ rank: 0, isPersonalBest: false, total: 0 }));
|
|
93
|
+
void handler(msg.boardId, msg.value, msg.key)
|
|
94
|
+
.then((result) => post({ rydr: true, type: "rydr/leaderboard.submitResult", nonce, result }))
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
console.error("[rydr-host] submitScore failed:", err);
|
|
97
|
+
post({ rydr: true, type: "rydr/leaderboard.submitResult", nonce, result: { rank: 0, isPersonalBest: false, total: 0 } });
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "rydr/leaderboard.query": {
|
|
102
|
+
const { nonce } = msg;
|
|
103
|
+
const handler = options.onGetLeaderboard ??
|
|
104
|
+
(async () => ({ entries: [] }));
|
|
105
|
+
void handler(msg.boardId, { key: msg.key, limit: msg.limit })
|
|
106
|
+
.then((page) => post({ rydr: true, type: "rydr/leaderboard.queryResult", nonce, entries: page.entries, you: page.you }))
|
|
107
|
+
.catch((err) => {
|
|
108
|
+
console.error("[rydr-host] getLeaderboard failed:", err);
|
|
109
|
+
post({ rydr: true, type: "rydr/leaderboard.queryResult", nonce, entries: [] });
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "rydr/run.save":
|
|
114
|
+
options.onSaveRun?.(msg.breakdown);
|
|
115
|
+
break;
|
|
116
|
+
case "rydr/gamedata.get":
|
|
117
|
+
case "rydr/gamedata.list":
|
|
118
|
+
case "rydr/gamedata.save":
|
|
119
|
+
case "rydr/gamedata.delete": {
|
|
120
|
+
const { nonce } = msg;
|
|
121
|
+
const kind = msg.type.slice("rydr/gamedata.".length);
|
|
122
|
+
const op = {
|
|
123
|
+
kind,
|
|
124
|
+
scope: msg.scope,
|
|
125
|
+
collection: msg.collection,
|
|
126
|
+
id: "id" in msg ? msg.id : undefined,
|
|
127
|
+
data: "data" in msg ? msg.data : undefined,
|
|
128
|
+
};
|
|
129
|
+
const handler = options.onGameData ?? (async () => ({ error: "gamedata not supported" }));
|
|
130
|
+
void handler(op)
|
|
131
|
+
.then((r) => post({ rydr: true, type: "rydr/gamedata.result", nonce, ...r }))
|
|
132
|
+
.catch((err) => {
|
|
133
|
+
console.error("[rydr-host] gamedata failed:", err);
|
|
134
|
+
post({ rydr: true, type: "rydr/gamedata.result", nonce, error: String(err) });
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "rydr/asset.uploadUrl": {
|
|
139
|
+
const { nonce } = msg;
|
|
140
|
+
const handler = options.onGetUploadUrl ?? (async () => ({ error: "asset upload not supported" }));
|
|
141
|
+
void handler({ collection: msg.collection, filename: msg.filename, contentType: msg.contentType })
|
|
142
|
+
.then((r) => post({ rydr: true, type: "rydr/asset.uploadUrlResult", nonce, ...r }))
|
|
143
|
+
.catch((err) => {
|
|
144
|
+
console.error("[rydr-host] asset.uploadUrl failed:", err);
|
|
145
|
+
post({ rydr: true, type: "rydr/asset.uploadUrlResult", nonce, error: String(err) });
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "rydr/replay.save": {
|
|
150
|
+
const { nonce } = msg;
|
|
151
|
+
const handler = options.onSaveReplay ?? (async () => ({ error: "replays not supported" }));
|
|
152
|
+
void handler(msg.runId, msg.blob, msg.meta)
|
|
153
|
+
.then((r) => post({ rydr: true, type: "rydr/replay.result", nonce, ...r }))
|
|
154
|
+
.catch((err) => {
|
|
155
|
+
console.error("[rydr-host] saveReplay failed:", err);
|
|
156
|
+
post({ rydr: true, type: "rydr/replay.result", nonce, error: String(err) });
|
|
157
|
+
});
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case "rydr/replay.get": {
|
|
161
|
+
const { nonce } = msg;
|
|
162
|
+
const handler = options.onGetReplay ?? (async () => ({ error: "replays not supported" }));
|
|
163
|
+
void handler(msg.runId)
|
|
164
|
+
.then((r) => post({ rydr: true, type: "rydr/replay.result", nonce, ...r }))
|
|
165
|
+
.catch((err) => {
|
|
166
|
+
console.error("[rydr-host] getReplay failed:", err);
|
|
167
|
+
post({ rydr: true, type: "rydr/replay.result", nonce, error: String(err) });
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case "rydr/run.get": {
|
|
172
|
+
const { nonce } = msg;
|
|
173
|
+
const handler = options.onGetRun ?? (async () => ({ error: "run.get not supported" }));
|
|
174
|
+
void handler(msg.runId)
|
|
175
|
+
.then((r) => post({ rydr: true, type: "rydr/run.result", nonce, ...r }))
|
|
176
|
+
.catch((err) => {
|
|
177
|
+
console.error("[rydr-host] getRun failed:", err);
|
|
178
|
+
post({ rydr: true, type: "rydr/run.result", nonce, error: String(err) });
|
|
179
|
+
});
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "rydr/room.join": {
|
|
183
|
+
const { roomId } = msg;
|
|
184
|
+
if (!options.onRoomJoin) {
|
|
185
|
+
post({ rydr: true, type: "rydr/room.closed", roomId });
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
roomControllers.get(roomId)?.leave();
|
|
189
|
+
const sink = {
|
|
190
|
+
opened: () => post({ rydr: true, type: "rydr/room.opened", roomId }),
|
|
191
|
+
closed: () => post({ rydr: true, type: "rydr/room.closed", roomId }),
|
|
192
|
+
presence: (members) => post({ rydr: true, type: "rydr/room.presence", roomId, members }),
|
|
193
|
+
state: (state) => post({ rydr: true, type: "rydr/room.state", roomId, state }),
|
|
194
|
+
message: (from, data) => post({ rydr: true, type: "rydr/room.message", roomId, from, data }),
|
|
195
|
+
telemetry: (r) => post({
|
|
196
|
+
rydr: true,
|
|
197
|
+
type: "rydr/room.telemetry",
|
|
198
|
+
roomId,
|
|
199
|
+
from: r.playerId,
|
|
200
|
+
power: r.power,
|
|
201
|
+
cadence: r.cadence,
|
|
202
|
+
heartRate: r.heartRate,
|
|
203
|
+
t: r.t,
|
|
204
|
+
}),
|
|
205
|
+
event: (e) => post({ rydr: true, type: "rydr/room.event", roomId, name: e.name, payload: e.payload, at: e.at, from: e.from }),
|
|
206
|
+
};
|
|
207
|
+
const controller = options.onRoomJoin(roomId, sink);
|
|
208
|
+
if (controller)
|
|
209
|
+
roomControllers.set(roomId, controller);
|
|
210
|
+
else
|
|
211
|
+
post({ rydr: true, type: "rydr/room.closed", roomId });
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case "rydr/room.send":
|
|
215
|
+
roomControllers.get(msg.roomId)?.send(msg.data);
|
|
216
|
+
break;
|
|
217
|
+
case "rydr/room.setState":
|
|
218
|
+
roomControllers.get(msg.roomId)?.setState(msg.patch);
|
|
219
|
+
break;
|
|
220
|
+
case "rydr/room.scheduleEvent":
|
|
221
|
+
roomControllers.get(msg.roomId)?.scheduleEvent(msg.name, msg.payload, msg.at);
|
|
222
|
+
break;
|
|
223
|
+
case "rydr/room.leave":
|
|
224
|
+
roomControllers.get(msg.roomId)?.leave();
|
|
225
|
+
roomControllers.delete(msg.roomId);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
window.addEventListener("message", onMessage);
|
|
230
|
+
return {
|
|
231
|
+
pause: () => post({ rydr: true, type: "rydr/lifecycle.pause" }),
|
|
232
|
+
resume: () => post({ rydr: true, type: "rydr/lifecycle.resume" }),
|
|
233
|
+
sendButton: (name, edge) => {
|
|
234
|
+
if (can("buttons"))
|
|
235
|
+
post({ rydr: true, type: "rydr/input.button", name, edge });
|
|
236
|
+
},
|
|
237
|
+
updateIdentity: (identity) => post({ rydr: true, type: "rydr/identity.update", identity }),
|
|
238
|
+
dispose: () => {
|
|
239
|
+
unsubscribeHardware?.();
|
|
240
|
+
unsubscribeHardware = null;
|
|
241
|
+
for (const controller of roomControllers.values())
|
|
242
|
+
controller.leave();
|
|
243
|
+
roomControllers.clear();
|
|
244
|
+
window.removeEventListener("message", onMessage);
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=PlatformHost.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlatformHost.js","sourceRoot":"","sources":["../../src/host/PlatformHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,cAAc,gBAAgB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rydr/game-sdk — client SDK for building games on the RYDR indoor-cycling platform.
|
|
3
|
+
*
|
|
4
|
+
* - `protocol` — the versioned platform↔game wire contract (handshake, capabilities,
|
|
5
|
+
* scoped identity, hardware/lifecycle messages, type guards).
|
|
6
|
+
* - `client` — `connectToPlatform()` → a `PlatformSession` (game side).
|
|
7
|
+
* - `host` — `createPlatformHost()` to embed a game from the shell (platform side).
|
|
8
|
+
*/
|
|
9
|
+
export * from "./protocol/index";
|
|
10
|
+
export * from "./client/index";
|
|
11
|
+
export * from "./host/index";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rydr/game-sdk — client SDK for building games on the RYDR indoor-cycling platform.
|
|
3
|
+
*
|
|
4
|
+
* - `protocol` — the versioned platform↔game wire contract (handshake, capabilities,
|
|
5
|
+
* scoped identity, hardware/lifecycle messages, type guards).
|
|
6
|
+
* - `client` — `connectToPlatform()` → a `PlatformSession` (game side).
|
|
7
|
+
* - `host` — `createPlatformHost()` to embed a game from the shell (platform side).
|
|
8
|
+
*/
|
|
9
|
+
export * from "./protocol/index";
|
|
10
|
+
export * from "./client/index";
|
|
11
|
+
export * from "./host/index";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leaderboard board definitions + value formatting — shared by the game manifest,
|
|
3
|
+
* a game's in-game results screen, and the platform's leaderboard hub.
|
|
4
|
+
*
|
|
5
|
+
* A board is declared ONCE (in the game's manifest, admin-managed) so every entry
|
|
6
|
+
* is comparable: `sort` answers "is more better?", `valueType` drives display
|
|
7
|
+
* formatting, `aggregate` collapses repeated submits. `submitScore(boardId, value)`
|
|
8
|
+
* sends a raw number in the board's unit; the platform ranks by `sort`, formats by
|
|
9
|
+
* `valueType` via {@link formatBoardValue}, and tie-breaks by earliest timestamp.
|
|
10
|
+
*/
|
|
11
|
+
/** Display/format hint for a board's raw numeric value. */
|
|
12
|
+
export type BoardValueType = "time" | "score" | "distance" | "speed" | "percent" | "duration" | "count";
|
|
13
|
+
/** Ranking direction. `asc` = lower wins (time); `desc` = higher wins (score). */
|
|
14
|
+
export type BoardSort = "asc" | "desc";
|
|
15
|
+
/** How repeated submits from the same player collapse. */
|
|
16
|
+
export type BoardAggregate = "best" | "last" | "sum";
|
|
17
|
+
/**
|
|
18
|
+
* A declarative leaderboard definition. Lives on the {@link GameManifest} (so the
|
|
19
|
+
* shell can render every game's boards with no game code) and is handed to the game
|
|
20
|
+
* at handshake as `session.boards`.
|
|
21
|
+
*/
|
|
22
|
+
export interface BoardDefinition {
|
|
23
|
+
/** Stable id; the `boardId` passed to `submitScore`/`getLeaderboard`. */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Human label for the board (defaults to `id`). */
|
|
26
|
+
label?: string;
|
|
27
|
+
valueType: BoardValueType;
|
|
28
|
+
sort: BoardSort;
|
|
29
|
+
aggregate: BoardAggregate;
|
|
30
|
+
}
|
|
31
|
+
/** One ranked leaderboard row. */
|
|
32
|
+
export interface BoardEntry {
|
|
33
|
+
/** 1-based rank within the board. */
|
|
34
|
+
rank: number;
|
|
35
|
+
playerId: string;
|
|
36
|
+
displayName: string;
|
|
37
|
+
/** Raw value in the board's unit (format with {@link formatBoardValue}). */
|
|
38
|
+
value: number;
|
|
39
|
+
/** The run this entry came from, if known (links to the FIT activity + run record). */
|
|
40
|
+
runId?: string;
|
|
41
|
+
/** ms timestamp the entry was recorded. */
|
|
42
|
+
ts: number;
|
|
43
|
+
}
|
|
44
|
+
/** Result of a `submitScore` — drives a post-game "you placed #N (new PB!)" screen. */
|
|
45
|
+
export interface SubmitScoreResult {
|
|
46
|
+
/** The submitter's 1-based rank after this submit. */
|
|
47
|
+
rank: number;
|
|
48
|
+
/** Whether this submit improved the player's standing on the board. */
|
|
49
|
+
isPersonalBest: boolean;
|
|
50
|
+
/** Total entrants on the board. */
|
|
51
|
+
total: number;
|
|
52
|
+
}
|
|
53
|
+
/** A page of leaderboard rows, plus the requesting player's own row when off-page. */
|
|
54
|
+
export interface LeaderboardPage {
|
|
55
|
+
entries: BoardEntry[];
|
|
56
|
+
/** The requester's own ranked row (may be outside `entries`). */
|
|
57
|
+
you?: BoardEntry;
|
|
58
|
+
}
|
|
59
|
+
/** Format a raw board value for display, per its {@link BoardValueType}. */
|
|
60
|
+
export declare function formatBoardValue(valueType: BoardValueType, value: number): string;
|
|
61
|
+
//# sourceMappingURL=boards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards.d.ts","sourceRoot":"","sources":["../../src/protocol/boards.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,OAAO,GACP,UAAU,GACV,OAAO,GACP,SAAS,GACT,UAAU,GACV,OAAO,CAAC;AAEZ,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvC,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAErD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,cAAc,CAAC;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,iEAAiE;IACjE,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBjF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leaderboard board definitions + value formatting — shared by the game manifest,
|
|
3
|
+
* a game's in-game results screen, and the platform's leaderboard hub.
|
|
4
|
+
*
|
|
5
|
+
* A board is declared ONCE (in the game's manifest, admin-managed) so every entry
|
|
6
|
+
* is comparable: `sort` answers "is more better?", `valueType` drives display
|
|
7
|
+
* formatting, `aggregate` collapses repeated submits. `submitScore(boardId, value)`
|
|
8
|
+
* sends a raw number in the board's unit; the platform ranks by `sort`, formats by
|
|
9
|
+
* `valueType` via {@link formatBoardValue}, and tie-breaks by earliest timestamp.
|
|
10
|
+
*/
|
|
11
|
+
/** Format a raw board value for display, per its {@link BoardValueType}. */
|
|
12
|
+
export function formatBoardValue(valueType, value) {
|
|
13
|
+
switch (valueType) {
|
|
14
|
+
case "time":
|
|
15
|
+
return formatClock(value, true);
|
|
16
|
+
case "duration":
|
|
17
|
+
return formatClock(value, false);
|
|
18
|
+
case "score":
|
|
19
|
+
case "count":
|
|
20
|
+
return Math.round(value).toLocaleString("en-US");
|
|
21
|
+
case "distance":
|
|
22
|
+
return value >= 1000
|
|
23
|
+
? `${(value / 1000).toFixed(1)} km`
|
|
24
|
+
: `${Math.round(value)} m`;
|
|
25
|
+
case "speed":
|
|
26
|
+
return `${(value * 3.6).toFixed(1)} km/h`;
|
|
27
|
+
case "percent":
|
|
28
|
+
return `${value.toFixed(1)}%`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Format milliseconds as a clock. `precise` (for `time` boards) appends hundredths
|
|
33
|
+
* and drops the hour unless needed: 83210 → "1:23.21". Coarse (for `duration`)
|
|
34
|
+
* rounds to whole seconds: 4985000 → "1:23:05".
|
|
35
|
+
*/
|
|
36
|
+
function formatClock(ms, precise) {
|
|
37
|
+
const totalMs = Math.max(0, ms);
|
|
38
|
+
const totalSeconds = Math.floor(totalMs / 1000);
|
|
39
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
40
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
41
|
+
const seconds = totalSeconds % 60;
|
|
42
|
+
if (precise) {
|
|
43
|
+
const hundredths = Math.floor((totalMs % 1000) / 10);
|
|
44
|
+
const secs = `${String(seconds).padStart(2, "0")}.${String(hundredths).padStart(2, "0")}`;
|
|
45
|
+
return hours > 0
|
|
46
|
+
? `${hours}:${String(minutes).padStart(2, "0")}:${secs}`
|
|
47
|
+
: `${minutes}:${secs}`;
|
|
48
|
+
}
|
|
49
|
+
return hours > 0
|
|
50
|
+
? `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`
|
|
51
|
+
: `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=boards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boards.js","sourceRoot":"","sources":["../../src/protocol/boards.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgEH,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,SAAyB,EAAE,KAAa;IACvE,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,KAAK,UAAU;YACb,OAAO,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACnC,KAAK,OAAO,CAAC;QACb,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACnD,KAAK,UAAU;YACb,OAAO,KAAK,IAAI,IAAI;gBAClB,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;gBACnC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;QAC/B,KAAK,OAAO;YACV,OAAO,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,KAAK,SAAS;YACZ,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,EAAU,EAAE,OAAgB;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAElC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QAC1F,OAAO,KAAK,GAAG,CAAC;YACd,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;YACxD,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,KAAK,GAAG,CAAC;QACd,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;QACpF,CAAC,CAAC,GAAG,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical, source-agnostic controller vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Every physical controller (phone, keyboard, Zwift Play) is normalised to
|
|
5
|
+
* these names by the platform before it reaches a game. The game never learns
|
|
6
|
+
* which device produced a press — only the canonical name and edge. This keeps
|
|
7
|
+
* games decoupled from hardware specifics and is the same vocabulary the
|
|
8
|
+
* platform's input layer already uses.
|
|
9
|
+
*/
|
|
10
|
+
export type ButtonName = "OK" | "UP" | "DOWN" | "LEFT" | "RIGHT" | "CANCEL";
|
|
11
|
+
/** Press = "down", release = "up". A held button emits one of each. */
|
|
12
|
+
export type ButtonEdge = "down" | "up";
|
|
13
|
+
//# sourceMappingURL=buttons.d.ts.map
|