@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.
Files changed (98) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -21
  3. package/README.md +191 -139
  4. package/dist/doctor/cli.mjs +49 -19
  5. package/dist/doctor/cli.mjs.map +1 -1
  6. package/dist/shared/client-version.gen.js +1 -1
  7. package/dist/shared/daily.js +1 -1
  8. package/dist/shared/globals.js +8 -3
  9. package/dist/shared/globals.js.map +1 -1
  10. package/dist/shared/index.d.ts +1 -1
  11. package/package.json +89 -89
  12. package/src/auto.ts +39 -39
  13. package/src/core/connection.ts +299 -299
  14. package/src/core/errors.ts +147 -147
  15. package/src/core/game-data.ts +38 -38
  16. package/src/core/hub-economy.ts +179 -179
  17. package/src/core/hub-progress.ts +205 -205
  18. package/src/core/hub.ts +131 -131
  19. package/src/core/menu-control.ts +61 -61
  20. package/src/core/offline/events.ts +50 -50
  21. package/src/core/offline/favorites.ts +21 -21
  22. package/src/core/offline/local-store.ts +261 -261
  23. package/src/core/offline/queue.ts +171 -171
  24. package/src/core/page.ts +74 -74
  25. package/src/core/rest/endpoint-types.ts +22 -22
  26. package/src/core/rest/rest-client.ts +152 -152
  27. package/src/core/storage/json-guards.ts +74 -74
  28. package/src/core/storage/key-value.ts +66 -66
  29. package/src/core/ticket.ts +43 -43
  30. package/src/core/types.ts +134 -134
  31. package/src/core/version/client-version.gen.ts +1 -1
  32. package/src/core/version/hub-version.ts +62 -62
  33. package/src/core/version/semver.ts +97 -97
  34. package/src/daily/daily-system.ts +161 -161
  35. package/src/daily/page-daily.ts +36 -36
  36. package/src/daily/types.ts +67 -67
  37. package/src/daily.ts +10 -10
  38. package/src/doctor/checks.ts +78 -76
  39. package/src/doctor/cli.ts +98 -106
  40. package/src/doctor/hub-version-fetch.ts +43 -0
  41. package/src/doctor/tsconfig.json +12 -12
  42. package/src/globals.ts +115 -110
  43. package/src/index.ts +50 -50
  44. package/src/input/device-state.ts +146 -146
  45. package/src/input/edges.ts +55 -55
  46. package/src/input/gamepad.ts +61 -61
  47. package/src/input/input-events.ts +32 -32
  48. package/src/input/input-registry.ts +164 -164
  49. package/src/input/input-system.ts +393 -393
  50. package/src/input/legacy-controls.ts +64 -64
  51. package/src/input/nav-state.ts +191 -191
  52. package/src/input/navigator.ts +171 -171
  53. package/src/input/on-screen-controls.ts +101 -101
  54. package/src/input/page-control.ts +43 -43
  55. package/src/input/page-input.ts +193 -193
  56. package/src/input/player-remaps.ts +77 -77
  57. package/src/input/source-values.ts +116 -116
  58. package/src/input/spatial.ts +47 -47
  59. package/src/input/types.ts +331 -331
  60. package/src/input/zero-config.ts +177 -177
  61. package/src/input.ts +22 -22
  62. package/src/mp.ts +10 -10
  63. package/src/overlay/controls-panel.ts +163 -163
  64. package/src/overlay/dom.ts +46 -46
  65. package/src/overlay/layout-clusters.ts +162 -162
  66. package/src/overlay/layout-geometry.ts +136 -136
  67. package/src/overlay/layout.ts +203 -203
  68. package/src/overlay/listener-scope.ts +23 -23
  69. package/src/overlay/on-screen-keyboard.ts +137 -137
  70. package/src/overlay/overlay.ts +208 -208
  71. package/src/overlay/styles.ts +9 -9
  72. package/src/overlay/virtual-controls.ts +262 -262
  73. package/src/race.ts +10 -10
  74. package/src/realtime/bridge.ts +121 -121
  75. package/src/realtime/cross-bundle.ts +29 -29
  76. package/src/realtime/fallback-notice.ts +48 -48
  77. package/src/realtime/hub-link.ts +65 -65
  78. package/src/realtime/latest-state.ts +33 -33
  79. package/src/realtime/multiplayer-forwarder.ts +28 -28
  80. package/src/realtime/multiplayer-types.ts +52 -52
  81. package/src/realtime/page-runtime.ts +60 -60
  82. package/src/realtime/race-forwarder.ts +59 -59
  83. package/src/realtime/race-types.ts +132 -132
  84. package/src/realtime/room-types.ts +125 -125
  85. package/src/realtime/rooms-forwarder.ts +79 -79
  86. package/src/realtime/runtime-api.ts +49 -49
  87. package/src/realtime/runtime-opener.ts +61 -61
  88. package/src/realtime/social-forwarder.ts +72 -72
  89. package/src/realtime/social-types.ts +78 -78
  90. package/src/realtime/types.ts +53 -53
  91. package/src/realtime/wire-types.ts +72 -72
  92. package/src/rooms.ts +10 -10
  93. package/src/social.ts +18 -18
  94. package/src/types.ts +13 -13
  95. package/src/uistack/page-stack.ts +45 -45
  96. package/src/uistack/play-suspension.ts +82 -82
  97. package/src/uistack/types.ts +76 -76
  98. package/src/uistack/ui-stack.ts +144 -144
@@ -1,137 +1,137 @@
1
- /**
2
- * The on-screen keyboard for text fields, usable by touch, mouse or gamepad
3
- * (the stick or d-pad moves between keys, A presses, B or Start closes).
4
- */
5
- import { RepeatingEdges, RisingEdges } from '../input/edges';
6
- import { isAnyPadButtonPressed, PAD_BUTTON, readGamepads } from '../input/gamepad';
7
- import type { Direction } from '../input/gamepad';
8
- import { findNearestInDirection, rectOf } from '../input/spatial';
9
- import { findElements } from './dom';
10
-
11
- const LETTER_ROWS = ['1234567890', 'qwertyuiop', 'asdfghjkl', 'zxcvbnm'];
12
- const STICK_THRESHOLD = 0.5;
13
-
14
- /** One key's markup. */
15
- function keyHtml(label: string, key: string, extraClass = ''): string {
16
- return `<button class="k ${extraClass}" data-k="${key}">${label}</button>`;
17
- }
18
-
19
- const KEYBOARD_HTML = `<div class="kb-rows">${LETTER_ROWS.map((row) => `<div class="kb-row">${Array.from(row).map((character) => keyHtml(character, character)).join('')}</div>`).join('')}`
20
- + `<div class="kb-row">${keyHtml('⇧', 'shift', 'wide')}${keyHtml('space', ' ', 'space')}${keyHtml('⌫', 'back', 'wide')}${keyHtml('↵', 'enter', 'wide')}${keyHtml('✕', 'close', 'wide')}</div></div>`;
21
-
22
- /** The direction the pads point, from the summed stick and d-pad. */
23
- function heldDirection(pads: readonly Gamepad[]): Direction | null {
24
- let horizontal = 0;
25
- let vertical = 0;
26
- for (const pad of pads) {
27
- horizontal += (pad.axes[0] ?? 0) - Number(pad.buttons[PAD_BUTTON.LEFT]?.pressed === true) + Number(pad.buttons[PAD_BUTTON.RIGHT]?.pressed === true);
28
- vertical += (pad.axes[1] ?? 0) - Number(pad.buttons[PAD_BUTTON.UP]?.pressed === true) + Number(pad.buttons[PAD_BUTTON.DOWN]?.pressed === true);
29
- }
30
- if (Math.abs(horizontal) <= STICK_THRESHOLD && Math.abs(vertical) <= STICK_THRESHOLD) {
31
- return null;
32
- }
33
- if (Math.abs(horizontal) > Math.abs(vertical)) {
34
- return horizontal > 0 ? 'right' : 'left';
35
- }
36
- return vertical > 0 ? 'down' : 'up';
37
- }
38
-
39
- /** The on-screen keyboard (one at a time, inside the overlay's shadow root). */
40
- export class OnScreenKeyboard {
41
- private readonly root: ShadowRoot;
42
- private panel: HTMLDivElement | null = null;
43
- private target: HTMLInputElement | HTMLTextAreaElement | null = null;
44
- private keys: HTMLElement[] = [];
45
- private focusedIndex = 0;
46
- private isShifted = false;
47
- private readonly repeats = new RepeatingEdges();
48
- private readonly presses = new RisingEdges();
49
-
50
- constructor(root: ShadowRoot) {
51
- this.root = root;
52
- }
53
-
54
- /** Whether the keyboard is showing. */
55
- get isOpen(): boolean {
56
- return this.panel !== null;
57
- }
58
-
59
- /** Open it for `target` (replacing any open keyboard). */
60
- open(target: HTMLInputElement | HTMLTextAreaElement | null): void {
61
- this.close();
62
- this.target = target;
63
- this.isShifted = false;
64
- this.focusedIndex = 0;
65
- const panel = document.createElement('div');
66
- panel.className = 'kb';
67
- panel.innerHTML = KEYBOARD_HTML;
68
- this.root.appendChild(panel);
69
- this.panel = panel;
70
- this.keys = findElements(panel, '.k', HTMLElement);
71
- for (const key of this.keys) {
72
- const press = (event: Event): void => {
73
- event.preventDefault();
74
- this.press(key.dataset['k'] ?? '');
75
- };
76
- key.addEventListener('mousedown', press);
77
- key.addEventListener('touchstart', press, { passive: false });
78
- }
79
- this.highlightFocusedKey();
80
- }
81
-
82
- /** Close it. */
83
- close(): void {
84
- this.panel?.remove();
85
- this.panel = null;
86
- this.target = null;
87
- this.keys = [];
88
- }
89
-
90
- /** Called every frame while open: gamepad navigation. */
91
- pollGamepads(): void {
92
- if (this.panel === null) {
93
- return;
94
- }
95
- const pads = readGamepads();
96
- const direction = heldDirection(pads);
97
- if (this.repeats.fires('move', direction !== null) && direction !== null) {
98
- this.moveFocus(direction);
99
- }
100
- if (this.presses.rose('act', isAnyPadButtonPressed(pads, PAD_BUTTON.A))) {
101
- this.press(this.keys[this.focusedIndex]?.dataset['k'] ?? '');
102
- }
103
- const isClosing = isAnyPadButtonPressed(pads, PAD_BUTTON.B) || isAnyPadButtonPressed(pads, PAD_BUTTON.START);
104
- if (this.presses.rose('close', isClosing)) {
105
- this.close();
106
- }
107
- }
108
-
109
- private press(key: string): void {
110
- if (key === 'close' || key === 'enter') {
111
- this.close();
112
- return;
113
- }
114
- if (key === 'shift') {
115
- this.isShifted = !this.isShifted;
116
- return;
117
- }
118
- const target = this.target;
119
- if (target === null || key === '') {
120
- return;
121
- }
122
- target.value = key === 'back' ? target.value.slice(0, -1) : target.value + (this.isShifted ? key.toUpperCase() : key);
123
- target.dispatchEvent(new Event('input', { bubbles: true }));
124
- }
125
-
126
- private moveFocus(direction: Direction): void {
127
- const next = findNearestInDirection(this.keys.map(rectOf), this.focusedIndex, direction);
128
- if (next >= 0) {
129
- this.focusedIndex = next;
130
- this.highlightFocusedKey();
131
- }
132
- }
133
-
134
- private highlightFocusedKey(): void {
135
- this.keys.forEach((key, index) => key.classList.toggle('kfocus', index === this.focusedIndex));
136
- }
137
- }
1
+ /**
2
+ * The on-screen keyboard for text fields, usable by touch, mouse or gamepad
3
+ * (the stick or d-pad moves between keys, A presses, B or Start closes).
4
+ */
5
+ import { RepeatingEdges, RisingEdges } from '../input/edges';
6
+ import { isAnyPadButtonPressed, PAD_BUTTON, readGamepads } from '../input/gamepad';
7
+ import type { Direction } from '../input/gamepad';
8
+ import { findNearestInDirection, rectOf } from '../input/spatial';
9
+ import { findElements } from './dom';
10
+
11
+ const LETTER_ROWS = ['1234567890', 'qwertyuiop', 'asdfghjkl', 'zxcvbnm'];
12
+ const STICK_THRESHOLD = 0.5;
13
+
14
+ /** One key's markup. */
15
+ function keyHtml(label: string, key: string, extraClass = ''): string {
16
+ return `<button class="k ${extraClass}" data-k="${key}">${label}</button>`;
17
+ }
18
+
19
+ const KEYBOARD_HTML = `<div class="kb-rows">${LETTER_ROWS.map((row) => `<div class="kb-row">${Array.from(row).map((character) => keyHtml(character, character)).join('')}</div>`).join('')}`
20
+ + `<div class="kb-row">${keyHtml('⇧', 'shift', 'wide')}${keyHtml('space', ' ', 'space')}${keyHtml('⌫', 'back', 'wide')}${keyHtml('↵', 'enter', 'wide')}${keyHtml('✕', 'close', 'wide')}</div></div>`;
21
+
22
+ /** The direction the pads point, from the summed stick and d-pad. */
23
+ function heldDirection(pads: readonly Gamepad[]): Direction | null {
24
+ let horizontal = 0;
25
+ let vertical = 0;
26
+ for (const pad of pads) {
27
+ horizontal += (pad.axes[0] ?? 0) - Number(pad.buttons[PAD_BUTTON.LEFT]?.pressed === true) + Number(pad.buttons[PAD_BUTTON.RIGHT]?.pressed === true);
28
+ vertical += (pad.axes[1] ?? 0) - Number(pad.buttons[PAD_BUTTON.UP]?.pressed === true) + Number(pad.buttons[PAD_BUTTON.DOWN]?.pressed === true);
29
+ }
30
+ if (Math.abs(horizontal) <= STICK_THRESHOLD && Math.abs(vertical) <= STICK_THRESHOLD) {
31
+ return null;
32
+ }
33
+ if (Math.abs(horizontal) > Math.abs(vertical)) {
34
+ return horizontal > 0 ? 'right' : 'left';
35
+ }
36
+ return vertical > 0 ? 'down' : 'up';
37
+ }
38
+
39
+ /** The on-screen keyboard (one at a time, inside the overlay's shadow root). */
40
+ export class OnScreenKeyboard {
41
+ private readonly root: ShadowRoot;
42
+ private panel: HTMLDivElement | null = null;
43
+ private target: HTMLInputElement | HTMLTextAreaElement | null = null;
44
+ private keys: HTMLElement[] = [];
45
+ private focusedIndex = 0;
46
+ private isShifted = false;
47
+ private readonly repeats = new RepeatingEdges();
48
+ private readonly presses = new RisingEdges();
49
+
50
+ constructor(root: ShadowRoot) {
51
+ this.root = root;
52
+ }
53
+
54
+ /** Whether the keyboard is showing. */
55
+ get isOpen(): boolean {
56
+ return this.panel !== null;
57
+ }
58
+
59
+ /** Open it for `target` (replacing any open keyboard). */
60
+ open(target: HTMLInputElement | HTMLTextAreaElement | null): void {
61
+ this.close();
62
+ this.target = target;
63
+ this.isShifted = false;
64
+ this.focusedIndex = 0;
65
+ const panel = document.createElement('div');
66
+ panel.className = 'kb';
67
+ panel.innerHTML = KEYBOARD_HTML;
68
+ this.root.appendChild(panel);
69
+ this.panel = panel;
70
+ this.keys = findElements(panel, '.k', HTMLElement);
71
+ for (const key of this.keys) {
72
+ const press = (event: Event): void => {
73
+ event.preventDefault();
74
+ this.press(key.dataset['k'] ?? '');
75
+ };
76
+ key.addEventListener('mousedown', press);
77
+ key.addEventListener('touchstart', press, { passive: false });
78
+ }
79
+ this.highlightFocusedKey();
80
+ }
81
+
82
+ /** Close it. */
83
+ close(): void {
84
+ this.panel?.remove();
85
+ this.panel = null;
86
+ this.target = null;
87
+ this.keys = [];
88
+ }
89
+
90
+ /** Called every frame while open: gamepad navigation. */
91
+ pollGamepads(): void {
92
+ if (this.panel === null) {
93
+ return;
94
+ }
95
+ const pads = readGamepads();
96
+ const direction = heldDirection(pads);
97
+ if (this.repeats.fires('move', direction !== null) && direction !== null) {
98
+ this.moveFocus(direction);
99
+ }
100
+ if (this.presses.rose('act', isAnyPadButtonPressed(pads, PAD_BUTTON.A))) {
101
+ this.press(this.keys[this.focusedIndex]?.dataset['k'] ?? '');
102
+ }
103
+ const isClosing = isAnyPadButtonPressed(pads, PAD_BUTTON.B) || isAnyPadButtonPressed(pads, PAD_BUTTON.START);
104
+ if (this.presses.rose('close', isClosing)) {
105
+ this.close();
106
+ }
107
+ }
108
+
109
+ private press(key: string): void {
110
+ if (key === 'close' || key === 'enter') {
111
+ this.close();
112
+ return;
113
+ }
114
+ if (key === 'shift') {
115
+ this.isShifted = !this.isShifted;
116
+ return;
117
+ }
118
+ const target = this.target;
119
+ if (target === null || key === '') {
120
+ return;
121
+ }
122
+ target.value = key === 'back' ? target.value.slice(0, -1) : target.value + (this.isShifted ? key.toUpperCase() : key);
123
+ target.dispatchEvent(new Event('input', { bubbles: true }));
124
+ }
125
+
126
+ private moveFocus(direction: Direction): void {
127
+ const next = findNearestInDirection(this.keys.map(rectOf), this.focusedIndex, direction);
128
+ if (next >= 0) {
129
+ this.focusedIndex = next;
130
+ this.highlightFocusedKey();
131
+ }
132
+ }
133
+
134
+ private highlightFocusedKey(): void {
135
+ this.keys.forEach((key, index) => key.classList.toggle('kfocus', index === this.focusedIndex));
136
+ }
137
+ }