homegames-client 0.1.1 → 0.1.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.
- package/dist/homegames-client.js +1 -1
- package/package.json +3 -3
- package/src/game-loader.js +9 -0
- package/src/game-view.js +7 -1
- package/src/index.js +1 -1
- package/src/input.js +29 -6
- package/src/local-game-chrome.js +1 -1
- package/src/local-game-loader.js +1 -0
- package/src/portable-project.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homegames-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Embeddable browser client for Homegames",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"browser": "dist/homegames-client.js",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"license": "GPL-3.0",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"buffer": "^6.0.3",
|
|
16
|
-
"homegames-common": "1.7.
|
|
17
|
-
"homepad": "0.2.
|
|
16
|
+
"homegames-common": "1.7.2",
|
|
17
|
+
"homepad": "0.2.1",
|
|
18
18
|
"qrcode-generator": "^2.0.4",
|
|
19
19
|
"squish-142": "npm:squishjs@1.4.2",
|
|
20
20
|
"squish-143": "npm:squishjs@1.4.3",
|
package/src/game-loader.js
CHANGED
|
@@ -57,6 +57,15 @@ const loadGameClass = ({ files, entryPoint }) => {
|
|
|
57
57
|
moduleCache[path] = module;
|
|
58
58
|
|
|
59
59
|
const source = normalized[path];
|
|
60
|
+
if (path.endsWith('.json')) {
|
|
61
|
+
try {
|
|
62
|
+
module.exports = JSON.parse(source);
|
|
63
|
+
return module.exports;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
delete moduleCache[path];
|
|
66
|
+
throw new Error('Invalid JSON module ' + path + ': ' + err.message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
60
69
|
const fn = new Function('module', 'exports', 'require', '__filename', '__dirname', source);
|
|
61
70
|
try {
|
|
62
71
|
fn(module, module.exports, requireShim, path, dir);
|
package/src/game-view.js
CHANGED
|
@@ -11,7 +11,13 @@ class GameView {
|
|
|
11
11
|
container.innerHTML = '';
|
|
12
12
|
Object.assign(container.style, { position: 'relative', overflow: 'hidden', background: '#000' });
|
|
13
13
|
this.canvas = document.createElement('canvas');
|
|
14
|
-
Object.assign(this.canvas.style, {
|
|
14
|
+
Object.assign(this.canvas.style, {
|
|
15
|
+
display: 'block', margin: '0 auto', cursor: 'default',
|
|
16
|
+
// Keep iOS tap highlights and long-press selection off the game surface.
|
|
17
|
+
webkitTapHighlightColor: 'transparent',
|
|
18
|
+
webkitTouchCallout: 'none',
|
|
19
|
+
webkitUserSelect: 'none', userSelect: 'none',
|
|
20
|
+
});
|
|
15
21
|
this.cursor = null;
|
|
16
22
|
this.cursorOverride = null;
|
|
17
23
|
this._cursorRaster = null;
|
package/src/index.js
CHANGED
|
@@ -111,7 +111,7 @@ class HomegamesClient {
|
|
|
111
111
|
onVisibility: this._shareOptions.room ? visibility => this._getRtcHost().setVisibility(visibility) : null,
|
|
112
112
|
onOpen: () => this._localSession?.inputHandler?.detach(),
|
|
113
113
|
onClose: () => this._localSession?.inputHandler?.attach(),
|
|
114
|
-
} : null, { onExports: () => this.viewExports(),
|
|
114
|
+
} : null, { author: game.author, onExports: () => this.viewExports(),
|
|
115
115
|
onOpen: () => this.setInputEnabled(false), onClose: () => this.setInputEnabled(true) });
|
|
116
116
|
this._configureToolbar(chrome.toolbar);
|
|
117
117
|
return chrome;
|
package/src/input.js
CHANGED
|
@@ -106,6 +106,7 @@ class InputHandler {
|
|
|
106
106
|
this._pressClickSent = false;
|
|
107
107
|
this.mousePos = this._hasPointerLock() ? this._canvasCenter() : [e.clientX, e.clientY];
|
|
108
108
|
this._lastClientPos = this.mousePos.slice();
|
|
109
|
+
this._pressOrigin = this.mousePos.slice();
|
|
109
110
|
this.assetManager.unlockAudio();
|
|
110
111
|
const captureGesture = this._canCapturePointer() && !this.spectating &&
|
|
111
112
|
e.target === this.canvas && e.button === 0 && !e.sourceCapabilities?.firesTouchEvents &&
|
|
@@ -178,6 +179,11 @@ class InputHandler {
|
|
|
178
179
|
return;
|
|
179
180
|
}
|
|
180
181
|
e.preventDefault();
|
|
182
|
+
// Claiming a canvas touch suppresses the synthesized mouse click
|
|
183
|
+
// that would normally focus an embedded game. Treat this direct
|
|
184
|
+
// gesture as input focus so the first tap can reach the game too.
|
|
185
|
+
// Page-control touches return above; blur still releases input.
|
|
186
|
+
this._focused = true;
|
|
181
187
|
this._touchActive = true;
|
|
182
188
|
clearTimeout(this.clickStopper); this.clickStopper = null;
|
|
183
189
|
this.assetManager.liveMedia?.hold('pointer');
|
|
@@ -185,6 +191,7 @@ class InputHandler {
|
|
|
185
191
|
this._pressClickSent = false;
|
|
186
192
|
this.mousePos = [e.touches[0].clientX + window.scrollX, e.touches[0].clientY + window.scrollY];
|
|
187
193
|
this._lastClientPos = [e.touches[0].clientX, e.touches[0].clientY];
|
|
194
|
+
this._pressOrigin = this.mousePos.slice();
|
|
188
195
|
this.assetManager.unlockAudio();
|
|
189
196
|
};
|
|
190
197
|
|
|
@@ -229,7 +236,7 @@ class InputHandler {
|
|
|
229
236
|
// report the release at the last known position so the server can
|
|
230
237
|
// close out the press (drag-end / offClick).
|
|
231
238
|
if (this.mouseDown && this._lastClientPos) {
|
|
232
|
-
this._sendMouseUp(this._lastClientPos[0], this._lastClientPos[1]);
|
|
239
|
+
this._sendMouseUp(this._lastClientPos[0], this._lastClientPos[1], true);
|
|
233
240
|
}
|
|
234
241
|
this.mouseDown = false;
|
|
235
242
|
};
|
|
@@ -408,8 +415,7 @@ class InputHandler {
|
|
|
408
415
|
// click contract those games' interactions were built on.
|
|
409
416
|
if (this.mouseDown && !this.clickStopper) {
|
|
410
417
|
if (!this._pressClickSent) {
|
|
411
|
-
this.
|
|
412
|
-
this._handleClick(clickInfo, true);
|
|
418
|
+
this._sendPendingClick(clickInfo);
|
|
413
419
|
} else if (this._hasPointerLock()) {
|
|
414
420
|
// Captured presses fire once; relative look is sent separately.
|
|
415
421
|
} else if (this._usesSingleClickContract()) {
|
|
@@ -476,7 +482,7 @@ class InputHandler {
|
|
|
476
482
|
_releasePointerPress() {
|
|
477
483
|
this._pointerLockOnRelease = false;
|
|
478
484
|
this.assetManager.liveMedia?.release('pointer');
|
|
479
|
-
if (this.mouseDown && this._lastClientPos) this._sendMouseUp(...this._lastClientPos);
|
|
485
|
+
if (this.mouseDown && this._lastClientPos) this._sendMouseUp(...this._lastClientPos, true);
|
|
480
486
|
this.mouseDown = false;
|
|
481
487
|
this._pressClickSent = false;
|
|
482
488
|
this.mousePos = null;
|
|
@@ -524,7 +530,7 @@ class InputHandler {
|
|
|
524
530
|
event.key === 'Escape' || event.key === 'Shift';
|
|
525
531
|
}
|
|
526
532
|
|
|
527
|
-
_sendMouseUp(clientX, clientY) {
|
|
533
|
+
_sendMouseUp(clientX, clientY, cancelled = false) {
|
|
528
534
|
const rect = this.canvas.getBoundingClientRect();
|
|
529
535
|
const clickX = (clientX - rect.left) / rect.width * 100;
|
|
530
536
|
const clickY = (clientY - rect.top) / rect.height * 100;
|
|
@@ -533,7 +539,16 @@ class InputHandler {
|
|
|
533
539
|
// (drag-end / offClick), even ones that finish outside the canvas.
|
|
534
540
|
const x = Math.min(99.99, Math.max(0, clickX));
|
|
535
541
|
const y = Math.min(99.99, Math.max(0, clickY));
|
|
536
|
-
|
|
542
|
+
// Preserve the last movement even when the whole drag happened between
|
|
543
|
+
// animation frames or inside the 30ms repeat throttle.
|
|
544
|
+
const origin = this._pressOrigin;
|
|
545
|
+
if (!cancelled && origin && this._pressClickSent && !this._hasPointerLock() &&
|
|
546
|
+
(Math.abs(clientX - origin[0]) > .5 || Math.abs(clientY - origin[1]) > .5)) {
|
|
547
|
+
this._sendMouseMove(clientX, clientY);
|
|
548
|
+
}
|
|
549
|
+
this._pressOrigin = null;
|
|
550
|
+
// End the press on blur/touchcancel without committing a drag-and-drop.
|
|
551
|
+
this.send(JSON.stringify({ type: 'mouseup', data: { x, y, ...(cancelled ? { cancelled: true } : {}) } }));
|
|
537
552
|
}
|
|
538
553
|
|
|
539
554
|
// Whether the game's squish version uses the single-click contract
|
|
@@ -572,7 +587,15 @@ class InputHandler {
|
|
|
572
587
|
_sendPendingClick(clickInfo) {
|
|
573
588
|
if (!this.mouseDown || this._pressClickSent || this.spectating || !this._focused) return;
|
|
574
589
|
this._pressClickSent = true;
|
|
590
|
+
// A pointer can move before the first animation frame. Its press still
|
|
591
|
+
// belongs to the original object, not whichever object it moved over.
|
|
592
|
+
const latest = this.mousePos;
|
|
593
|
+
if (this._pressOrigin) {
|
|
594
|
+
this.mousePos = this._pressOrigin.slice();
|
|
595
|
+
clickInfo = this._canClick(this.mousePos[0], this.mousePos[1]);
|
|
596
|
+
}
|
|
575
597
|
this._handleClick(clickInfo, true);
|
|
598
|
+
if (this._attached) this.mousePos = latest;
|
|
576
599
|
}
|
|
577
600
|
|
|
578
601
|
_promptTextInput(nodeId) {
|
package/src/local-game-chrome.js
CHANGED
|
@@ -7,7 +7,7 @@ const createLocalChrome = (containerId, title, onHome, share = null, options = {
|
|
|
7
7
|
container.innerHTML = '';
|
|
8
8
|
const releaseStyles = useFrameStyles();
|
|
9
9
|
const frame = document.createElement('div'); frame.className = 'hg-session-frame';
|
|
10
|
-
const toolbar = createPlayerToolbar(window, { title, home: onHome, status: options.status || 'Playing locally',
|
|
10
|
+
const toolbar = createPlayerToolbar(window, { title, author: options.author, home: onHome, status: options.status || 'Playing locally',
|
|
11
11
|
onOpen: options.onOpen || share?.onOpen, onClose: options.onClose || share?.onClose });
|
|
12
12
|
toolbar.bar.classList.add('hg-session-bar');
|
|
13
13
|
if (options.onExports) toolbar.setAction('exports', { label: 'View exports', run: options.onExports });
|
package/src/local-game-loader.js
CHANGED
|
@@ -15,6 +15,7 @@ const loadLocalGame = async ({ manifestUrl, gameKey, versionId, baseUrl } = {},
|
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
17
17
|
name: manifest.name,
|
|
18
|
+
author: manifest.author || manifest.createdBy,
|
|
18
19
|
multiplayer: manifest.multiplayer === true,
|
|
19
20
|
files: manifest.files,
|
|
20
21
|
entryPoint: manifest.entryPoint,
|
package/src/portable-project.js
CHANGED
|
@@ -9,6 +9,8 @@ const saves = require('homegames-common/portable-saves');
|
|
|
9
9
|
const portableClient = require('homegames-common/portable-client');
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
12
|
+
...require('homegames-common/studio-debug'),
|
|
13
|
+
...require('homegames-common/studio-handoff'),
|
|
12
14
|
create: files => project.createProject(files, options),
|
|
13
15
|
importArchive: bytes => project.importArchive(bytes, options),
|
|
14
16
|
exportArchive: project.exportArchive,
|
|
@@ -36,5 +38,6 @@ module.exports = {
|
|
|
36
38
|
readHtmlArchive: html.readHtmlArchive,
|
|
37
39
|
htmlBytesLimit: html.htmlBytesLimit,
|
|
38
40
|
checkSyntax: project.checkSyntax,
|
|
41
|
+
thumbnailPath: project.thumbnailPath,
|
|
39
42
|
prepareTrustedLocalGame: (value, settings) => prepareTrustedLocalGame(value, squishMap, settings),
|
|
40
43
|
};
|