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

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 (154) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -21
  3. package/README.md +206 -139
  4. package/dist/auto.d.ts +6 -6
  5. package/dist/auto.js +2 -2
  6. package/dist/doctor/cli.mjs +62 -25
  7. package/dist/doctor/cli.mjs.map +1 -1
  8. package/dist/index.d.ts +6 -6
  9. package/dist/index.js +2 -2
  10. package/dist/mp.d.ts +3 -3
  11. package/dist/race.d.ts +1 -1
  12. package/dist/rooms.d.ts +3 -3
  13. package/dist/shared/bridge.js +9 -3
  14. package/dist/shared/bridge.js.map +1 -1
  15. package/dist/shared/client-version.gen.js +1 -1
  16. package/dist/shared/daily.d.ts +10 -4
  17. package/dist/shared/daily.d.ts.map +1 -1
  18. package/dist/shared/daily.js +12 -2
  19. package/dist/shared/daily.js.map +1 -1
  20. package/dist/shared/globals.js +8 -3
  21. package/dist/shared/globals.js.map +1 -1
  22. package/dist/shared/index.d.ts +314 -35
  23. package/dist/shared/index.d.ts.map +1 -1
  24. package/dist/shared/input.js +7 -2
  25. package/dist/shared/input.js.map +1 -1
  26. package/dist/shared/latest-state.js +1 -1
  27. package/dist/shared/mp.js +10 -2
  28. package/dist/shared/mp.js.map +1 -1
  29. package/dist/shared/multiplayer-types.d.ts +15 -2
  30. package/dist/shared/multiplayer-types.d.ts.map +1 -1
  31. package/dist/shared/race-types.d.ts +1 -1
  32. package/dist/shared/race.js +1 -1
  33. package/dist/shared/room-types.d.ts +28 -3
  34. package/dist/shared/room-types.d.ts.map +1 -1
  35. package/dist/shared/rooms.d.ts.map +1 -1
  36. package/dist/shared/rooms.js +10 -2
  37. package/dist/shared/rooms.js.map +1 -1
  38. package/dist/shared/runtime-opener.js +1 -1
  39. package/dist/shared/social.d.ts +23 -8
  40. package/dist/shared/social.d.ts.map +1 -1
  41. package/dist/shared/social.js +6 -1
  42. package/dist/shared/social.js.map +1 -1
  43. package/dist/shared/src.js +1588 -388
  44. package/dist/shared/src.js.map +1 -1
  45. package/dist/shared/wire-types.d.ts +6 -2
  46. package/dist/shared/wire-types.d.ts.map +1 -1
  47. package/dist/social.d.ts +5 -5
  48. package/dist/social.js +1 -1
  49. package/package.json +89 -89
  50. package/src/auto.ts +39 -39
  51. package/src/core/connection.ts +394 -299
  52. package/src/core/errors.ts +147 -147
  53. package/src/core/game-data.ts +40 -38
  54. package/src/core/hub-economy.ts +216 -179
  55. package/src/core/hub-progress.ts +202 -205
  56. package/src/core/hub.ts +133 -131
  57. package/src/core/menu-control.ts +61 -61
  58. package/src/core/offline/events.ts +50 -50
  59. package/src/core/offline/favorites.ts +21 -21
  60. package/src/core/offline/hub-kinds/daily-kind.ts +68 -0
  61. package/src/core/offline/hub-kinds/economy-kinds.ts +86 -0
  62. package/src/core/offline/hub-kinds/game-fields.ts +2 -0
  63. package/src/core/offline/hub-kinds/index.ts +35 -0
  64. package/src/core/offline/hub-kinds/legacy-ops.ts +37 -0
  65. package/src/core/offline/hub-kinds/progress-kinds.ts +148 -0
  66. package/src/core/offline/kind-adapters.ts +48 -0
  67. package/src/core/offline/local-inventory.ts +64 -0
  68. package/src/core/offline/local-store.ts +259 -261
  69. package/src/core/offline/offline-api.ts +65 -0
  70. package/src/core/offline/outbox-entry.ts +59 -0
  71. package/src/core/offline/outbox-kinds.ts +78 -0
  72. package/src/core/offline/outbox-merge.ts +39 -0
  73. package/src/core/offline/outbox-replayer.ts +141 -0
  74. package/src/core/offline/outbox-store.ts +85 -0
  75. package/src/core/offline/outbox.ts +232 -0
  76. package/src/core/offline/queue.ts +126 -171
  77. package/src/core/offline/replay-lease.ts +87 -0
  78. package/src/core/offline/replay-policy.ts +52 -0
  79. package/src/core/page.ts +74 -74
  80. package/src/core/rest/endpoint-types.ts +22 -22
  81. package/src/core/rest/rest-client.ts +163 -152
  82. package/src/core/rest/routes.gen.ts +108 -87
  83. package/src/core/storage/json-guards.ts +80 -74
  84. package/src/core/storage/key-value.ts +66 -66
  85. package/src/core/ticket.ts +43 -43
  86. package/src/core/types.ts +134 -134
  87. package/src/core/version/client-version.gen.ts +1 -1
  88. package/src/core/version/hub-version.ts +62 -62
  89. package/src/core/version/semver.ts +97 -97
  90. package/src/daily/daily-system.ts +168 -161
  91. package/src/daily/page-daily.ts +36 -36
  92. package/src/daily/types.ts +68 -67
  93. package/src/daily.ts +10 -10
  94. package/src/doctor/checks.ts +86 -76
  95. package/src/doctor/cli.ts +98 -106
  96. package/src/doctor/hub-version-fetch.ts +43 -0
  97. package/src/doctor/tsconfig.json +12 -12
  98. package/src/globals.ts +115 -110
  99. package/src/index.ts +55 -50
  100. package/src/input/device-state.ts +146 -146
  101. package/src/input/edges.ts +55 -55
  102. package/src/input/gamepad.ts +61 -61
  103. package/src/input/input-events.ts +32 -32
  104. package/src/input/input-registry.ts +171 -164
  105. package/src/input/input-system.ts +393 -393
  106. package/src/input/legacy-controls.ts +64 -64
  107. package/src/input/nav-state.ts +191 -191
  108. package/src/input/navigator.ts +171 -171
  109. package/src/input/on-screen-controls.ts +101 -101
  110. package/src/input/page-control.ts +43 -43
  111. package/src/input/page-input.ts +193 -193
  112. package/src/input/player-remaps.ts +77 -77
  113. package/src/input/source-values.ts +116 -116
  114. package/src/input/spatial.ts +47 -47
  115. package/src/input/types.ts +331 -331
  116. package/src/input/zero-config.ts +177 -177
  117. package/src/input.ts +22 -22
  118. package/src/mp.ts +10 -10
  119. package/src/overlay/controls-panel.ts +163 -163
  120. package/src/overlay/dom.ts +46 -46
  121. package/src/overlay/layout-clusters.ts +162 -162
  122. package/src/overlay/layout-geometry.ts +136 -136
  123. package/src/overlay/layout.ts +203 -203
  124. package/src/overlay/listener-scope.ts +23 -23
  125. package/src/overlay/on-screen-keyboard.ts +137 -137
  126. package/src/overlay/overlay.ts +208 -208
  127. package/src/overlay/styles.ts +9 -9
  128. package/src/overlay/virtual-controls.ts +262 -262
  129. package/src/race.ts +10 -10
  130. package/src/realtime/bridge.ts +126 -121
  131. package/src/realtime/cross-bundle.ts +29 -29
  132. package/src/realtime/fallback-notice.ts +48 -48
  133. package/src/realtime/hub-link.ts +65 -65
  134. package/src/realtime/latest-state.ts +33 -33
  135. package/src/realtime/multiplayer-forwarder.ts +40 -28
  136. package/src/realtime/multiplayer-types.ts +66 -52
  137. package/src/realtime/page-runtime.ts +60 -60
  138. package/src/realtime/race-forwarder.ts +59 -59
  139. package/src/realtime/race-types.ts +132 -132
  140. package/src/realtime/room-types.ts +151 -125
  141. package/src/realtime/rooms-forwarder.ts +91 -79
  142. package/src/realtime/runtime-api.ts +60 -49
  143. package/src/realtime/runtime-opener.ts +61 -61
  144. package/src/realtime/social-forwarder.ts +77 -72
  145. package/src/realtime/social-types.ts +86 -78
  146. package/src/realtime/types.ts +53 -53
  147. package/src/realtime/wire-types.ts +79 -72
  148. package/src/rooms.ts +10 -10
  149. package/src/social.ts +18 -18
  150. package/src/types.ts +13 -13
  151. package/src/uistack/page-stack.ts +45 -45
  152. package/src/uistack/play-suspension.ts +82 -82
  153. package/src/uistack/types.ts +76 -76
  154. 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
+ }