@diffenderfer-games/hub 2.0.0-next.0 → 2.0.0-next.1
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 +9 -0
- package/LICENSE +21 -21
- package/README.md +191 -139
- package/dist/doctor/cli.mjs +49 -19
- package/dist/doctor/cli.mjs.map +1 -1
- package/dist/shared/client-version.gen.js +1 -1
- package/dist/shared/daily.js +1 -1
- package/dist/shared/globals.js +8 -3
- package/dist/shared/globals.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/package.json +89 -89
- package/src/auto.ts +39 -39
- package/src/core/connection.ts +299 -299
- package/src/core/errors.ts +147 -147
- package/src/core/game-data.ts +38 -38
- package/src/core/hub-economy.ts +179 -179
- package/src/core/hub-progress.ts +205 -205
- package/src/core/hub.ts +131 -131
- package/src/core/menu-control.ts +61 -61
- package/src/core/offline/events.ts +50 -50
- package/src/core/offline/favorites.ts +21 -21
- package/src/core/offline/local-store.ts +261 -261
- package/src/core/offline/queue.ts +171 -171
- package/src/core/page.ts +74 -74
- package/src/core/rest/endpoint-types.ts +22 -22
- package/src/core/rest/rest-client.ts +152 -152
- package/src/core/storage/json-guards.ts +74 -74
- package/src/core/storage/key-value.ts +66 -66
- package/src/core/ticket.ts +43 -43
- package/src/core/types.ts +134 -134
- package/src/core/version/client-version.gen.ts +1 -1
- package/src/core/version/hub-version.ts +62 -62
- package/src/core/version/semver.ts +97 -97
- package/src/daily/daily-system.ts +161 -161
- package/src/daily/page-daily.ts +36 -36
- package/src/daily/types.ts +67 -67
- package/src/daily.ts +10 -10
- package/src/doctor/checks.ts +78 -76
- package/src/doctor/cli.ts +98 -106
- package/src/doctor/hub-version-fetch.ts +43 -0
- package/src/doctor/tsconfig.json +12 -12
- package/src/globals.ts +115 -110
- package/src/index.ts +50 -50
- package/src/input/device-state.ts +146 -146
- package/src/input/edges.ts +55 -55
- package/src/input/gamepad.ts +61 -61
- package/src/input/input-events.ts +32 -32
- package/src/input/input-registry.ts +164 -164
- package/src/input/input-system.ts +393 -393
- package/src/input/legacy-controls.ts +64 -64
- package/src/input/nav-state.ts +191 -191
- package/src/input/navigator.ts +171 -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 +177 -177
- package/src/input.ts +22 -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 +208 -208
- package/src/overlay/styles.ts +9 -9
- package/src/overlay/virtual-controls.ts +262 -262
- package/src/race.ts +10 -10
- package/src/realtime/bridge.ts +121 -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 +28 -28
- package/src/realtime/multiplayer-types.ts +52 -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 +125 -125
- package/src/realtime/rooms-forwarder.ts +79 -79
- package/src/realtime/runtime-api.ts +49 -49
- package/src/realtime/runtime-opener.ts +61 -61
- package/src/realtime/social-forwarder.ts +72 -72
- package/src/realtime/social-types.ts +78 -78
- package/src/realtime/types.ts +53 -53
- package/src/realtime/wire-types.ts +72 -72
- package/src/rooms.ts +10 -10
- package/src/social.ts +18 -18
- 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
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hub relay rooms as games see them through `hub.rooms`: realtime
|
|
3
|
-
* multiplayer for static games, relayed (never refereed) by the hub.
|
|
4
|
-
*/
|
|
5
|
-
import type { Json, RoomInfo } from '@diffenderfer-games/hub-contract';
|
|
6
|
-
import type { RoomChatLine, RoomMessage } from './wire-types';
|
|
7
|
-
|
|
8
|
-
/** The shared room state (a JSON object, at most 32 KB). */
|
|
9
|
-
export type RoomState = Record<string, Json>;
|
|
10
|
-
|
|
11
|
-
/** Options for `hub.rooms.create`. `game` defaults to the current slug. */
|
|
12
|
-
export interface RoomCreateOptions {
|
|
13
|
-
/** The game slug (default: this page's game). */
|
|
14
|
-
game?: string;
|
|
15
|
-
/** The game mode. */
|
|
16
|
-
mode?: string;
|
|
17
|
-
/** Listed in `hub.rooms.list` (default false). */
|
|
18
|
-
public?: boolean;
|
|
19
|
-
/** Seats. */
|
|
20
|
-
maxPlayers?: number;
|
|
21
|
-
/** `host` (default): guests' unaddressed sends reach the host only. `relay`: everyone hears every send. */
|
|
22
|
-
model?: 'host' | 'relay';
|
|
23
|
-
/** Seat this party in the room. */
|
|
24
|
-
partyId?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Options for joining a room or race. */
|
|
28
|
-
export interface JoinOptions {
|
|
29
|
-
/** Watch instead of taking a seat (if the game allows spectators). */
|
|
30
|
-
spectate?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Events a {@link HubRoom} emits, name → payload. */
|
|
34
|
-
export interface RoomEvents {
|
|
35
|
-
/** The room changed: roster, ready flags, phase, host. */
|
|
36
|
-
room: RoomInfo;
|
|
37
|
-
/** A relayed game message. */
|
|
38
|
-
msg: RoomMessage;
|
|
39
|
-
/** The shared state after a change. */
|
|
40
|
-
state: RoomState;
|
|
41
|
-
/** A moderated chat line. */
|
|
42
|
-
chat: RoomChatLine;
|
|
43
|
-
/** The host removed me. */
|
|
44
|
-
kicked: { roomId: string };
|
|
45
|
-
/**
|
|
46
|
-
* The room is gone for this tab. `reason`: empty, ended, host_left,
|
|
47
|
-
* other_tab, moved, removed, or `gone` (client-side: the seat couldn't be
|
|
48
|
-
* reclaimed after a reconnect, e.g. the host restarted).
|
|
49
|
-
*/
|
|
50
|
-
closed: { reason: string };
|
|
51
|
-
/** The host ended the match (phase → `ended`); `results` is what it passed to `end()`. Fires once. */
|
|
52
|
-
ended: { room: RoomInfo; results?: Json };
|
|
53
|
-
/** A player left mid-game (or their reconnect grace ran out). */
|
|
54
|
-
abandoned: { userId: number; reason?: string };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Who I am in a room. */
|
|
58
|
-
export interface RoomSelf {
|
|
59
|
-
/** My user id. */
|
|
60
|
-
userId: number;
|
|
61
|
-
/** I host the room. */
|
|
62
|
-
host: boolean;
|
|
63
|
-
/** I'm watching, not seated. */
|
|
64
|
-
spectator: boolean;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Options for {@link HubRoom.send}. */
|
|
68
|
-
export interface RoomSendOptions {
|
|
69
|
-
/** Send to this seat only. */
|
|
70
|
-
to?: number;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Options for {@link HubRoom.setState}. */
|
|
74
|
-
export interface RoomStateOptions {
|
|
75
|
-
/** Replace the whole state instead of shallow-merging the patch. */
|
|
76
|
-
replace?: boolean;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** A live hub relay room, as games see it. */
|
|
80
|
-
export interface HubRoom {
|
|
81
|
-
/** The room as last reported. */
|
|
82
|
-
readonly info: RoomInfo;
|
|
83
|
-
/** The shared room state (at most 32 KB). */
|
|
84
|
-
readonly state: RoomState;
|
|
85
|
-
/** Who I am in this room. */
|
|
86
|
-
readonly me: RoomSelf;
|
|
87
|
-
/** Subscribe to a room event. Returns the unsubscribe function. */
|
|
88
|
-
on<Name extends keyof RoomEvents>(event: Name, listener: (payload: RoomEvents[Name]) => void): () => void;
|
|
89
|
-
/** Mark my seat ready (or not). */
|
|
90
|
-
ready(isReady: boolean): Promise<void>;
|
|
91
|
-
/** Set my seat's small game data (team, colour…), at most 2 KB. */
|
|
92
|
-
seat(seatData: Json): Promise<void>;
|
|
93
|
-
/** Host: start the match. */
|
|
94
|
-
start(): Promise<void>;
|
|
95
|
-
/** Relay a game message. */
|
|
96
|
-
send(message: Json, options?: RoomSendOptions): Promise<void>;
|
|
97
|
-
/** Change the shared state. */
|
|
98
|
-
setState(patch: RoomState, options?: RoomStateOptions): Promise<void>;
|
|
99
|
-
/** Free-text chat; resolves with the delivered (maybe softened) text, rejects with a content rejection. */
|
|
100
|
-
chat(text: string): Promise<string>;
|
|
101
|
-
/** Send a quick-chat phrase. */
|
|
102
|
-
quick(phraseId: string): Promise<void>;
|
|
103
|
-
/** Host: remove a player. */
|
|
104
|
-
kick(userId: number): Promise<void>;
|
|
105
|
-
/** Leave the room. */
|
|
106
|
-
leave(): Promise<void>;
|
|
107
|
-
/** Host: end the match, with optional results for everyone. */
|
|
108
|
-
end(results?: Json): Promise<void>;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Options for `hub.rooms.list`. */
|
|
112
|
-
export interface RoomListOptions {
|
|
113
|
-
/** Only rooms in this mode. */
|
|
114
|
-
mode?: string;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** `hub.rooms`: hub relay rooms (static games, no server). */
|
|
118
|
-
export interface HubRoomsApi {
|
|
119
|
-
/** Create a room and take its first seat. */
|
|
120
|
-
create(options?: RoomCreateOptions): Promise<HubRoom>;
|
|
121
|
-
/** Join a room by its 5-character code or its id. */
|
|
122
|
-
join(codeOrId: string, options?: JoinOptions): Promise<HubRoom>;
|
|
123
|
-
/** Public rooms in the lobby phase for this game. */
|
|
124
|
-
list(options?: RoomListOptions): Promise<RoomInfo[]>;
|
|
125
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Hub relay rooms as games see them through `hub.rooms`: realtime
|
|
3
|
+
* multiplayer for static games, relayed (never refereed) by the hub.
|
|
4
|
+
*/
|
|
5
|
+
import type { Json, RoomInfo } from '@diffenderfer-games/hub-contract';
|
|
6
|
+
import type { RoomChatLine, RoomMessage } from './wire-types';
|
|
7
|
+
|
|
8
|
+
/** The shared room state (a JSON object, at most 32 KB). */
|
|
9
|
+
export type RoomState = Record<string, Json>;
|
|
10
|
+
|
|
11
|
+
/** Options for `hub.rooms.create`. `game` defaults to the current slug. */
|
|
12
|
+
export interface RoomCreateOptions {
|
|
13
|
+
/** The game slug (default: this page's game). */
|
|
14
|
+
game?: string;
|
|
15
|
+
/** The game mode. */
|
|
16
|
+
mode?: string;
|
|
17
|
+
/** Listed in `hub.rooms.list` (default false). */
|
|
18
|
+
public?: boolean;
|
|
19
|
+
/** Seats. */
|
|
20
|
+
maxPlayers?: number;
|
|
21
|
+
/** `host` (default): guests' unaddressed sends reach the host only. `relay`: everyone hears every send. */
|
|
22
|
+
model?: 'host' | 'relay';
|
|
23
|
+
/** Seat this party in the room. */
|
|
24
|
+
partyId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Options for joining a room or race. */
|
|
28
|
+
export interface JoinOptions {
|
|
29
|
+
/** Watch instead of taking a seat (if the game allows spectators). */
|
|
30
|
+
spectate?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Events a {@link HubRoom} emits, name → payload. */
|
|
34
|
+
export interface RoomEvents {
|
|
35
|
+
/** The room changed: roster, ready flags, phase, host. */
|
|
36
|
+
room: RoomInfo;
|
|
37
|
+
/** A relayed game message. */
|
|
38
|
+
msg: RoomMessage;
|
|
39
|
+
/** The shared state after a change. */
|
|
40
|
+
state: RoomState;
|
|
41
|
+
/** A moderated chat line. */
|
|
42
|
+
chat: RoomChatLine;
|
|
43
|
+
/** The host removed me. */
|
|
44
|
+
kicked: { roomId: string };
|
|
45
|
+
/**
|
|
46
|
+
* The room is gone for this tab. `reason`: empty, ended, host_left,
|
|
47
|
+
* other_tab, moved, removed, or `gone` (client-side: the seat couldn't be
|
|
48
|
+
* reclaimed after a reconnect, e.g. the host restarted).
|
|
49
|
+
*/
|
|
50
|
+
closed: { reason: string };
|
|
51
|
+
/** The host ended the match (phase → `ended`); `results` is what it passed to `end()`. Fires once. */
|
|
52
|
+
ended: { room: RoomInfo; results?: Json };
|
|
53
|
+
/** A player left mid-game (or their reconnect grace ran out). */
|
|
54
|
+
abandoned: { userId: number; reason?: string };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Who I am in a room. */
|
|
58
|
+
export interface RoomSelf {
|
|
59
|
+
/** My user id. */
|
|
60
|
+
userId: number;
|
|
61
|
+
/** I host the room. */
|
|
62
|
+
host: boolean;
|
|
63
|
+
/** I'm watching, not seated. */
|
|
64
|
+
spectator: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Options for {@link HubRoom.send}. */
|
|
68
|
+
export interface RoomSendOptions {
|
|
69
|
+
/** Send to this seat only. */
|
|
70
|
+
to?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Options for {@link HubRoom.setState}. */
|
|
74
|
+
export interface RoomStateOptions {
|
|
75
|
+
/** Replace the whole state instead of shallow-merging the patch. */
|
|
76
|
+
replace?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A live hub relay room, as games see it. */
|
|
80
|
+
export interface HubRoom {
|
|
81
|
+
/** The room as last reported. */
|
|
82
|
+
readonly info: RoomInfo;
|
|
83
|
+
/** The shared room state (at most 32 KB). */
|
|
84
|
+
readonly state: RoomState;
|
|
85
|
+
/** Who I am in this room. */
|
|
86
|
+
readonly me: RoomSelf;
|
|
87
|
+
/** Subscribe to a room event. Returns the unsubscribe function. */
|
|
88
|
+
on<Name extends keyof RoomEvents>(event: Name, listener: (payload: RoomEvents[Name]) => void): () => void;
|
|
89
|
+
/** Mark my seat ready (or not). */
|
|
90
|
+
ready(isReady: boolean): Promise<void>;
|
|
91
|
+
/** Set my seat's small game data (team, colour…), at most 2 KB. */
|
|
92
|
+
seat(seatData: Json): Promise<void>;
|
|
93
|
+
/** Host: start the match. */
|
|
94
|
+
start(): Promise<void>;
|
|
95
|
+
/** Relay a game message. */
|
|
96
|
+
send(message: Json, options?: RoomSendOptions): Promise<void>;
|
|
97
|
+
/** Change the shared state. */
|
|
98
|
+
setState(patch: RoomState, options?: RoomStateOptions): Promise<void>;
|
|
99
|
+
/** Free-text chat; resolves with the delivered (maybe softened) text, rejects with a content rejection. */
|
|
100
|
+
chat(text: string): Promise<string>;
|
|
101
|
+
/** Send a quick-chat phrase. */
|
|
102
|
+
quick(phraseId: string): Promise<void>;
|
|
103
|
+
/** Host: remove a player. */
|
|
104
|
+
kick(userId: number): Promise<void>;
|
|
105
|
+
/** Leave the room. */
|
|
106
|
+
leave(): Promise<void>;
|
|
107
|
+
/** Host: end the match, with optional results for everyone. */
|
|
108
|
+
end(results?: Json): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Options for `hub.rooms.list`. */
|
|
112
|
+
export interface RoomListOptions {
|
|
113
|
+
/** Only rooms in this mode. */
|
|
114
|
+
mode?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** `hub.rooms`: hub relay rooms (static games, no server). */
|
|
118
|
+
export interface HubRoomsApi {
|
|
119
|
+
/** Create a room and take its first seat. */
|
|
120
|
+
create(options?: RoomCreateOptions): Promise<HubRoom>;
|
|
121
|
+
/** Join a room by its 5-character code or its id. */
|
|
122
|
+
join(codeOrId: string, options?: JoinOptions): Promise<HubRoom>;
|
|
123
|
+
/** Public rooms in the lobby phase for this game. */
|
|
124
|
+
list(options?: RoomListOptions): Promise<RoomInfo[]>;
|
|
125
|
+
}
|
|
@@ -1,79 +1,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.
|
|
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.
|
|
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,49 +1,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 { 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 { 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,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
|
+
}
|