@worldmesh/runtime 0.1.0 → 0.2.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 +21 -2
- package/dist/avatar/descriptor.d.ts +27 -0
- package/dist/avatar/descriptor.d.ts.map +1 -0
- package/dist/avatar/descriptor.js +41 -0
- package/dist/avatar/descriptor.js.map +1 -0
- package/dist/avatar/handoff.d.ts +29 -0
- package/dist/avatar/handoff.d.ts.map +1 -0
- package/dist/avatar/handoff.js +92 -0
- package/dist/avatar/handoff.js.map +1 -0
- package/dist/avatar/loader.d.ts +26 -0
- package/dist/avatar/loader.d.ts.map +1 -0
- package/dist/avatar/loader.js +177 -0
- package/dist/avatar/loader.js.map +1 -0
- package/dist/controls/input.d.ts +25 -0
- package/dist/controls/input.d.ts.map +1 -1
- package/dist/controls/input.js +60 -2
- package/dist/controls/input.js.map +1 -1
- package/dist/controls/touch.d.ts.map +1 -1
- package/dist/controls/touch.js +5 -0
- package/dist/controls/touch.js.map +1 -1
- package/dist/core/worldmesh.d.ts.map +1 -1
- package/dist/core/worldmesh.js +114 -8
- package/dist/core/worldmesh.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/movement/controller.d.ts +10 -1
- package/dist/movement/controller.d.ts.map +1 -1
- package/dist/movement/controller.js +61 -4
- package/dist/movement/controller.js.map +1 -1
- package/dist/net/adapter.d.ts +3 -14
- package/dist/net/adapter.d.ts.map +1 -1
- package/dist/net/adapter.js.map +1 -1
- package/dist/net/presence.d.ts +68 -0
- package/dist/net/presence.d.ts.map +1 -0
- package/dist/net/presence.js +425 -0
- package/dist/net/presence.js.map +1 -0
- package/dist/player/avatar.d.ts +4 -0
- package/dist/player/avatar.d.ts.map +1 -1
- package/dist/player/avatar.js +97 -4
- package/dist/player/avatar.js.map +1 -1
- package/dist/player/player.d.ts +13 -0
- package/dist/player/player.d.ts.map +1 -1
- package/dist/player/player.js +44 -5
- package/dist/player/player.js.map +1 -1
- package/dist/portals/portals.d.ts +4 -1
- package/dist/portals/portals.d.ts.map +1 -1
- package/dist/portals/portals.js +6 -2
- package/dist/portals/portals.js.map +1 -1
- package/dist/types.d.ts +78 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/overlay.d.ts +7 -0
- package/dist/ui/overlay.d.ts.map +1 -1
- package/dist/ui/overlay.js +62 -7
- package/dist/ui/overlay.js.map +1 -1
- package/package.json +4 -1
- package/src/avatar/descriptor.ts +56 -0
- package/src/avatar/handoff.ts +91 -0
- package/src/avatar/loader.ts +184 -0
- package/src/controls/input.ts +61 -2
- package/src/controls/touch.ts +5 -0
- package/src/core/worldmesh.ts +110 -8
- package/src/index.ts +21 -1
- package/src/movement/controller.ts +59 -5
- package/src/net/adapter.ts +3 -14
- package/src/net/presence.ts +498 -0
- package/src/player/avatar.ts +109 -4
- package/src/player/player.ts +42 -3
- package/src/portals/portals.ts +6 -2
- package/src/types.ts +77 -4
- package/src/ui/overlay.ts +60 -7
|
@@ -13,6 +13,12 @@ export const DEFAULT_TUNING = {
|
|
|
13
13
|
maxFallSpeed: 55,
|
|
14
14
|
fallLimit: -60,
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* How fast two people who start out inside each other (both arriving at the
|
|
18
|
+
* spawn point, say) drift apart, in m/s. Slow, so it reads as making room
|
|
19
|
+
* rather than a shove.
|
|
20
|
+
*/
|
|
21
|
+
const SEPARATE_SPEED = 1.5;
|
|
16
22
|
/**
|
|
17
23
|
* The one movement implementation every WorldMesh world shares.
|
|
18
24
|
* Optional capabilities are branches on `abilities` flags, never subclasses:
|
|
@@ -27,6 +33,8 @@ export class MovementController {
|
|
|
27
33
|
flying = false;
|
|
28
34
|
/** Current standing height, shrinks while crouching. */
|
|
29
35
|
currentHeight;
|
|
36
|
+
/** Other people's bodies, which this player walks into rather than through. */
|
|
37
|
+
bodies = null;
|
|
30
38
|
tuning;
|
|
31
39
|
input;
|
|
32
40
|
collision;
|
|
@@ -161,12 +169,20 @@ export class MovementController {
|
|
|
161
169
|
}
|
|
162
170
|
applyMotion(dt) {
|
|
163
171
|
const horizontal = this.scratch.set(this.velocity.x * dt, 0, this.velocity.z * dt);
|
|
164
|
-
|
|
172
|
+
let resolved = this.collision.resolveHorizontal(this.position, horizontal, this.radius, this.currentHeight);
|
|
173
|
+
const walked = resolved.clone();
|
|
174
|
+
const apart = this.keepApart(resolved, dt);
|
|
175
|
+
// Stepping round someone can lead into a wall, so check the walls again.
|
|
176
|
+
if (apart !== 'clear') {
|
|
177
|
+
resolved = this.collision.resolveHorizontal(this.position, resolved, this.radius, this.currentHeight);
|
|
178
|
+
}
|
|
165
179
|
// Feed the resolved slide back into velocity so we do not keep pushing
|
|
166
|
-
// into a wall and accelerating along it.
|
|
180
|
+
// into a wall and accelerating along it. Drifting out of someone is not
|
|
181
|
+
// walking, so it does not carry on once clear of them.
|
|
167
182
|
if (dt > 0) {
|
|
168
|
-
|
|
169
|
-
this.velocity.
|
|
183
|
+
const moved = apart === 'separating' ? walked : resolved;
|
|
184
|
+
this.velocity.x = moved.x / dt;
|
|
185
|
+
this.velocity.z = moved.z / dt;
|
|
170
186
|
}
|
|
171
187
|
this.position.x += resolved.x;
|
|
172
188
|
this.position.z += resolved.z;
|
|
@@ -196,5 +212,46 @@ export class MovementController {
|
|
|
196
212
|
this.onGround = false;
|
|
197
213
|
}
|
|
198
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Bend this step's horizontal `move` so the player does not end up inside
|
|
217
|
+
* anyone else. Walking into someone stops at their edge and slides round
|
|
218
|
+
* them; already overlapping, the gap may only grow. Says whether `move`
|
|
219
|
+
* changed, and whether that includes drifting out of someone.
|
|
220
|
+
*/
|
|
221
|
+
keepApart(move, dt) {
|
|
222
|
+
if (!this.bodies)
|
|
223
|
+
return 'clear';
|
|
224
|
+
const { x, y, z } = this.position;
|
|
225
|
+
let result = 'clear';
|
|
226
|
+
for (const body of this.bodies()) {
|
|
227
|
+
// Only bodies at the same height block: jumping over someone is fine.
|
|
228
|
+
if (body.y >= y + this.currentHeight || y >= body.y + body.height)
|
|
229
|
+
continue;
|
|
230
|
+
const reach = this.radius + body.radius;
|
|
231
|
+
const nowX = x - body.x;
|
|
232
|
+
const nowZ = z - body.z;
|
|
233
|
+
const now = Math.hypot(nowX, nowZ);
|
|
234
|
+
if (now >= reach + Math.hypot(move.x, move.z))
|
|
235
|
+
continue;
|
|
236
|
+
let awayX = nowX + move.x;
|
|
237
|
+
let awayZ = nowZ + move.z;
|
|
238
|
+
const next = Math.hypot(awayX, awayZ);
|
|
239
|
+
const floor = Math.min(reach, now + SEPARATE_SPEED * dt);
|
|
240
|
+
if (next >= floor)
|
|
241
|
+
continue;
|
|
242
|
+
if (next < 1e-4) {
|
|
243
|
+
// Heading straight through their middle: step back the way we came, or any way at all.
|
|
244
|
+
awayX = now > 1e-4 ? nowX : 1;
|
|
245
|
+
awayZ = now > 1e-4 ? nowZ : 0;
|
|
246
|
+
}
|
|
247
|
+
// End the step `floor` away from them, in the direction we were headed.
|
|
248
|
+
const away = Math.hypot(awayX, awayZ);
|
|
249
|
+
move.x = (awayX / away) * floor - nowX;
|
|
250
|
+
move.z = (awayZ / away) * floor - nowZ;
|
|
251
|
+
if (result !== 'separating')
|
|
252
|
+
result = now < reach ? 'separating' : 'blocked';
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
199
256
|
}
|
|
200
257
|
//# sourceMappingURL=controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/movement/controller.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/movement/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/net/adapter.d.ts
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import type { NetworkAdapter, PlayerState } from '../types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - the local player is simulated from serializable `PlayerState`, so a peer's
|
|
7
|
-
* state can drive an identical avatar with no movement-code changes;
|
|
8
|
-
* - the runtime exposes `update(dt)` separately from rendering, so a fixed
|
|
9
|
-
* network tick can drive it;
|
|
10
|
-
* - nothing in movement/camera/portals reaches for a global connection.
|
|
11
|
-
*
|
|
12
|
-
* Expected eventual topologies, in order of how much WorldMesh has to host:
|
|
13
|
-
* 1. peer-to-peer WebRTC, worlds exchange state directly;
|
|
14
|
-
* 2. a multiplayer server run by the world's own creator;
|
|
15
|
-
* 3. optional WorldMesh signaling/discovery only (no game traffic);
|
|
16
|
-
* 4. an optional hosted fallback, last resort.
|
|
3
|
+
* The built-in implementation is `Presence` (presence.ts), enabled with the
|
|
4
|
+
* `multiplayer` option. Anything else that implements `NetworkAdapter` can be
|
|
5
|
+
* passed as `network` instead.
|
|
17
6
|
*/
|
|
18
7
|
export type { NetworkAdapter, PlayerState };
|
|
19
8
|
/** Round trip a player state over the wire. Kept tiny on purpose. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/net/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/net/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;GAIG;AACH,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC;AAE5C,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAE/D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAEnE"}
|
package/dist/net/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/net/adapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/net/adapter.ts"],"names":[],"mappings":"AASA,qEAAqE;AACrE,MAAM,UAAU,oBAAoB,CAAC,KAAkB;IACrD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Object3D, type Scene } from 'three';
|
|
2
|
+
import type { NetworkAdapter, PeerBody, PlayerState, WorldMeshHandle } from '../types.js';
|
|
3
|
+
/** The relay WorldMesh hosts (workers/presence). Worlds can point at their own. */
|
|
4
|
+
export declare const DEFAULT_PRESENCE_SERVER = "wss://worldmesh-presence.elias-willnat.workers.dev";
|
|
5
|
+
export interface PresenceOptions {
|
|
6
|
+
/** Full WebSocket URL of the room, e.g. `wss://relay.example/world`. */
|
|
7
|
+
url: string;
|
|
8
|
+
scene: Scene;
|
|
9
|
+
/** Players in the room including this one, or null while disconnected. */
|
|
10
|
+
onCount?: (count: number | null) => void;
|
|
11
|
+
getName?: () => string | null;
|
|
12
|
+
/** Made-up name shown instead of the username in private mode. */
|
|
13
|
+
getAlias?: () => string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Someone new just started walking here, first seen at (x, z). Not called
|
|
16
|
+
* for the people already in the room when this visitor joins.
|
|
17
|
+
*/
|
|
18
|
+
onArrive?: (x: number, z: number) => void;
|
|
19
|
+
/** Called with each remote figure as it is made, e.g. to add an outline. */
|
|
20
|
+
onFigure?: (root: Object3D) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Shows everyone else who is walking the same room, through the runtime's
|
|
24
|
+
* NetworkAdapter seam. The connection is best-effort: if the presence server
|
|
25
|
+
* is unreachable the lobby simply stays single-player.
|
|
26
|
+
*/
|
|
27
|
+
export declare class Presence implements NetworkAdapter {
|
|
28
|
+
private socket;
|
|
29
|
+
private selfId;
|
|
30
|
+
private remotes;
|
|
31
|
+
private group;
|
|
32
|
+
private unsubscribe;
|
|
33
|
+
private backoff;
|
|
34
|
+
private reconnectTimer;
|
|
35
|
+
private closed;
|
|
36
|
+
private lastSent;
|
|
37
|
+
private lastPayload;
|
|
38
|
+
private url;
|
|
39
|
+
private onCount;
|
|
40
|
+
private getName;
|
|
41
|
+
private getAlias;
|
|
42
|
+
private onArrive;
|
|
43
|
+
private onFigure;
|
|
44
|
+
constructor(options: PresenceOptions);
|
|
45
|
+
attach(world: WorldMeshHandle): void;
|
|
46
|
+
sendLocalState(state: PlayerState): void;
|
|
47
|
+
/** Say something. Everyone else gets a bubble; this client draws its own. */
|
|
48
|
+
say(text: string): void;
|
|
49
|
+
/** Everyone else's body where it is drawn, so walking into them stops where they appear to be. */
|
|
50
|
+
bodies(): PeerBody[];
|
|
51
|
+
/** Ease remote avatars toward their last known position. Driven by the world's update event. */
|
|
52
|
+
update(dt: number): void;
|
|
53
|
+
/**
|
|
54
|
+
* Leave and re-enter the room as a new peer. Everyone else sees this
|
|
55
|
+
* visitor leave and someone new arrive, with nothing linking the two.
|
|
56
|
+
*/
|
|
57
|
+
rejoin(): void;
|
|
58
|
+
detach(): void;
|
|
59
|
+
private connect;
|
|
60
|
+
private disconnect;
|
|
61
|
+
private scheduleReconnect;
|
|
62
|
+
private handleMessage;
|
|
63
|
+
private upsert;
|
|
64
|
+
private clearRemotes;
|
|
65
|
+
private handlePageHide;
|
|
66
|
+
private handlePageShow;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/net/presence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkF,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC;AAElI,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1F,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,uDAAuD,CAAC;AAE5F,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,0EAA0E;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;CACrC;AAwDD;;;;GAIG;AACH,qBAAa,QAAS,YAAW,cAAc;IAC7C,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,WAAW,CAAM;IAEzB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,OAAO,EAAE,eAAe;IAWpC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAUpC,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAgBxC,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOvB,kGAAkG;IAClG,MAAM,IAAI,QAAQ,EAAE;IASpB,gGAAgG;IAChG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IA+BxB;;;OAGG;IACH,MAAM,IAAI,IAAI;IAQd,MAAM,IAAI,IAAI;IAUd,OAAO,CAAC,OAAO;IAiCf,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,MAAM;IA6Cd,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,cAAc,CAGpB;IAEF,OAAO,CAAC,cAAc,CAKpB;CACH"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { CanvasTexture, Group, Mesh, MeshBasicMaterial, Sprite, SpriteMaterial, Vector3 } from 'three';
|
|
2
|
+
import { animateDefaultAvatar, createDefaultAvatar, setAvatarExpression } from '../player/avatar.js';
|
|
3
|
+
/** The relay WorldMesh hosts (workers/presence). Worlds can point at their own. */
|
|
4
|
+
export const DEFAULT_PRESENCE_SERVER = 'wss://worldmesh-presence.elias-willnat.workers.dev';
|
|
5
|
+
/** How often the local position goes out, per second. */
|
|
6
|
+
const SEND_RATE = 10;
|
|
7
|
+
/** Resend at least this often while standing still, so late joiners see us. */
|
|
8
|
+
const HEARTBEAT_MS = 3000;
|
|
9
|
+
const MAX_BACKOFF_MS = 30_000;
|
|
10
|
+
/**
|
|
11
|
+
* For peers that do not send `g`: a height change smaller than this between
|
|
12
|
+
* two updates is standing, not jumping or falling.
|
|
13
|
+
*/
|
|
14
|
+
const GROUND_HOLD = 0.02;
|
|
15
|
+
/** The size of a remote figure for collisions: the default player's. */
|
|
16
|
+
const BODY_RADIUS = 0.35;
|
|
17
|
+
const BODY_HEIGHT = 1.8;
|
|
18
|
+
/** Height of the name tag's centre above the avatar's feet. */
|
|
19
|
+
const TAG_HEIGHT = 2.15;
|
|
20
|
+
/**
|
|
21
|
+
* Shows everyone else who is walking the same room, through the runtime's
|
|
22
|
+
* NetworkAdapter seam. The connection is best-effort: if the presence server
|
|
23
|
+
* is unreachable the lobby simply stays single-player.
|
|
24
|
+
*/
|
|
25
|
+
export class Presence {
|
|
26
|
+
socket = null;
|
|
27
|
+
selfId = null;
|
|
28
|
+
remotes = new Map();
|
|
29
|
+
group = new Group();
|
|
30
|
+
unsubscribe = null;
|
|
31
|
+
backoff = 1000;
|
|
32
|
+
reconnectTimer = 0;
|
|
33
|
+
closed = false;
|
|
34
|
+
lastSent = 0;
|
|
35
|
+
lastPayload = '';
|
|
36
|
+
url;
|
|
37
|
+
onCount;
|
|
38
|
+
getName;
|
|
39
|
+
getAlias;
|
|
40
|
+
onArrive;
|
|
41
|
+
onFigure;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.url = options.url;
|
|
44
|
+
this.onCount = options.onCount ?? (() => { });
|
|
45
|
+
this.getName = options.getName ?? (() => null);
|
|
46
|
+
this.getAlias = options.getAlias ?? (() => null);
|
|
47
|
+
this.onArrive = options.onArrive ?? (() => { });
|
|
48
|
+
this.onFigure = options.onFigure ?? (() => { });
|
|
49
|
+
this.group.name = 'worldmesh:remote-players';
|
|
50
|
+
options.scene.add(this.group);
|
|
51
|
+
}
|
|
52
|
+
attach(world) {
|
|
53
|
+
this.unsubscribe = world.on('update', ({ dt, state }) => {
|
|
54
|
+
this.sendLocalState(state);
|
|
55
|
+
this.update(dt);
|
|
56
|
+
});
|
|
57
|
+
window.addEventListener('pagehide', this.handlePageHide);
|
|
58
|
+
window.addEventListener('pageshow', this.handlePageShow);
|
|
59
|
+
this.connect();
|
|
60
|
+
}
|
|
61
|
+
sendLocalState(state) {
|
|
62
|
+
const socket = this.socket;
|
|
63
|
+
if (!socket || socket.readyState !== WebSocket.OPEN || !this.selfId)
|
|
64
|
+
return;
|
|
65
|
+
const now = performance.now();
|
|
66
|
+
if (now - this.lastSent < 1000 / SEND_RATE)
|
|
67
|
+
return;
|
|
68
|
+
const [x, y, z] = state.position;
|
|
69
|
+
const payload = JSON.stringify({ t: 's', p: [round(x), round(y), round(z)], r: round(state.facing), e: state.expression, n: this.getName() ?? '', a: this.getAlias() ?? '', g: state.onGround || state.flying ? 1 : 0 });
|
|
70
|
+
if (payload === this.lastPayload && now - this.lastSent < HEARTBEAT_MS)
|
|
71
|
+
return;
|
|
72
|
+
socket.send(payload);
|
|
73
|
+
this.lastPayload = payload;
|
|
74
|
+
this.lastSent = now;
|
|
75
|
+
}
|
|
76
|
+
/** Say something. Everyone else gets a bubble; this client draws its own. */
|
|
77
|
+
say(text) {
|
|
78
|
+
const socket = this.socket;
|
|
79
|
+
const line = text.replace(/[\u0000-\u001f]/g, '').trim().slice(0, 80);
|
|
80
|
+
if (!line || !socket || socket.readyState !== WebSocket.OPEN)
|
|
81
|
+
return;
|
|
82
|
+
socket.send(JSON.stringify({ t: 'c', m: line }));
|
|
83
|
+
}
|
|
84
|
+
/** Everyone else's body where it is drawn, so walking into them stops where they appear to be. */
|
|
85
|
+
bodies() {
|
|
86
|
+
const bodies = [];
|
|
87
|
+
for (const { root } of this.remotes.values()) {
|
|
88
|
+
const { x, y, z } = root.position;
|
|
89
|
+
bodies.push({ x, y, z, radius: BODY_RADIUS, height: BODY_HEIGHT });
|
|
90
|
+
}
|
|
91
|
+
return bodies;
|
|
92
|
+
}
|
|
93
|
+
/** Ease remote avatars toward their last known position. Driven by the world's update event. */
|
|
94
|
+
update(dt) {
|
|
95
|
+
const blend = 1 - Math.exp(-dt * 10);
|
|
96
|
+
for (const remote of this.remotes.values()) {
|
|
97
|
+
const before = remote.root.position.clone();
|
|
98
|
+
remote.root.position.lerp(remote.target, blend);
|
|
99
|
+
let delta = remote.targetYaw - remote.yaw;
|
|
100
|
+
delta = Math.atan2(Math.sin(delta), Math.cos(delta));
|
|
101
|
+
remote.yaw += delta * blend;
|
|
102
|
+
remote.root.rotation.y = remote.yaw;
|
|
103
|
+
const moved = Math.hypot(remote.root.position.x - before.x, remote.root.position.z - before.z);
|
|
104
|
+
remote.speed += (moved / Math.max(dt, 1e-4) - remote.speed) * blend;
|
|
105
|
+
animateDefaultAvatar(remote.root, { dt, speed: remote.speed, grounded: remote.grounded });
|
|
106
|
+
if (remote.bubble) {
|
|
107
|
+
remote.bubbleLeft -= dt;
|
|
108
|
+
remote.bubble.material.opacity = remote.bubbleLeft < 1 ? Math.max(0, remote.bubbleLeft) : 1;
|
|
109
|
+
if (remote.bubbleLeft <= 0)
|
|
110
|
+
dropBubble(remote);
|
|
111
|
+
}
|
|
112
|
+
// The frame that crosses the end of the fade lands on fully opaque, however long it was.
|
|
113
|
+
const fading = remote.appear < APPEAR_FADE;
|
|
114
|
+
remote.appear += dt;
|
|
115
|
+
if (fading) {
|
|
116
|
+
const t = Math.min(1, remote.appear / APPEAR_FADE);
|
|
117
|
+
setFigureOpacity(remote.root, t * t * (3 - 2 * t));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Leave and re-enter the room as a new peer. Everyone else sees this
|
|
123
|
+
* visitor leave and someone new arrive, with nothing linking the two.
|
|
124
|
+
*/
|
|
125
|
+
rejoin() {
|
|
126
|
+
if (this.closed)
|
|
127
|
+
return;
|
|
128
|
+
window.clearTimeout(this.reconnectTimer);
|
|
129
|
+
this.disconnect();
|
|
130
|
+
this.backoff = 1000;
|
|
131
|
+
this.connect();
|
|
132
|
+
}
|
|
133
|
+
detach() {
|
|
134
|
+
this.closed = true;
|
|
135
|
+
window.clearTimeout(this.reconnectTimer);
|
|
136
|
+
window.removeEventListener('pagehide', this.handlePageHide);
|
|
137
|
+
window.removeEventListener('pageshow', this.handlePageShow);
|
|
138
|
+
this.unsubscribe?.();
|
|
139
|
+
this.disconnect();
|
|
140
|
+
this.group.removeFromParent();
|
|
141
|
+
}
|
|
142
|
+
connect() {
|
|
143
|
+
if (this.closed || this.socket)
|
|
144
|
+
return;
|
|
145
|
+
let socket;
|
|
146
|
+
try {
|
|
147
|
+
socket = new WebSocket(this.url);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
this.scheduleReconnect();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.socket = socket;
|
|
154
|
+
socket.addEventListener('message', (event) => {
|
|
155
|
+
if (typeof event.data !== 'string')
|
|
156
|
+
return;
|
|
157
|
+
let message;
|
|
158
|
+
try {
|
|
159
|
+
message = JSON.parse(event.data);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.handleMessage(message);
|
|
165
|
+
});
|
|
166
|
+
socket.addEventListener('close', () => {
|
|
167
|
+
if (this.socket !== socket)
|
|
168
|
+
return;
|
|
169
|
+
this.socket = null;
|
|
170
|
+
this.selfId = null;
|
|
171
|
+
this.clearRemotes();
|
|
172
|
+
this.onCount(null);
|
|
173
|
+
this.scheduleReconnect();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
disconnect() {
|
|
177
|
+
const socket = this.socket;
|
|
178
|
+
this.socket = null;
|
|
179
|
+
this.selfId = null;
|
|
180
|
+
socket?.close(1000, 'bye');
|
|
181
|
+
this.clearRemotes();
|
|
182
|
+
this.onCount(null);
|
|
183
|
+
}
|
|
184
|
+
scheduleReconnect() {
|
|
185
|
+
if (this.closed)
|
|
186
|
+
return;
|
|
187
|
+
window.clearTimeout(this.reconnectTimer);
|
|
188
|
+
this.reconnectTimer = window.setTimeout(() => this.connect(), this.backoff);
|
|
189
|
+
this.backoff = Math.min(this.backoff * 2, MAX_BACKOFF_MS);
|
|
190
|
+
}
|
|
191
|
+
handleMessage(message) {
|
|
192
|
+
switch (message.t) {
|
|
193
|
+
case 'welcome':
|
|
194
|
+
this.selfId = message.id;
|
|
195
|
+
this.backoff = 1000;
|
|
196
|
+
this.lastPayload = '';
|
|
197
|
+
this.lastSent = 0;
|
|
198
|
+
for (const peer of message.peers)
|
|
199
|
+
this.upsert(peer.id, peer, true);
|
|
200
|
+
break;
|
|
201
|
+
case 's':
|
|
202
|
+
if (message.id !== this.selfId)
|
|
203
|
+
this.upsert(message.id, message, false);
|
|
204
|
+
break;
|
|
205
|
+
case 'c': {
|
|
206
|
+
const remote = message.id === this.selfId ? undefined : this.remotes.get(message.id);
|
|
207
|
+
if (remote && typeof message.m === 'string')
|
|
208
|
+
showBubble(remote, message.m);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case 'leave': {
|
|
212
|
+
const remote = this.remotes.get(message.id);
|
|
213
|
+
if (remote)
|
|
214
|
+
disposeObject(remote.root);
|
|
215
|
+
this.remotes.delete(message.id);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
this.onCount(this.remotes.size + 1);
|
|
220
|
+
}
|
|
221
|
+
upsert(id, state, snap) {
|
|
222
|
+
const { p, r: yaw, e: expression, g } = state;
|
|
223
|
+
if (!Array.isArray(p) || p.length !== 3 || !p.every(Number.isFinite) || !Number.isFinite(yaw))
|
|
224
|
+
return;
|
|
225
|
+
const label = nameLabel(state.n, state.a);
|
|
226
|
+
let remote = this.remotes.get(id);
|
|
227
|
+
if (!remote) {
|
|
228
|
+
const root = createDefaultAvatar();
|
|
229
|
+
const tag = createNameTag(GUEST);
|
|
230
|
+
tag.position.y = TAG_HEIGHT;
|
|
231
|
+
root.add(tag);
|
|
232
|
+
this.onFigure(root);
|
|
233
|
+
this.group.add(root);
|
|
234
|
+
remote = { root, target: new Vector3(p[0], p[1], p[2]), targetYaw: yaw, yaw, speed: 0, grounded: true, label: GUEST, tag, appear: 0, bubble: null, bubbleLeft: 0 };
|
|
235
|
+
this.remotes.set(id, remote);
|
|
236
|
+
setFigureOpacity(root, 0);
|
|
237
|
+
if (!snap)
|
|
238
|
+
this.onArrive(p[0], p[2]);
|
|
239
|
+
snap = true;
|
|
240
|
+
}
|
|
241
|
+
// Peers that do not say whether they are grounded count as standing when
|
|
242
|
+
// their height holds still, so a raised floor like the spawn platform
|
|
243
|
+
// does not leave them in the jump pose.
|
|
244
|
+
remote.grounded = g === 0 || g === 1 ? g === 1 : p[1] < 0.05 || Math.abs(p[1] - remote.target.y) < GROUND_HOLD;
|
|
245
|
+
remote.target.set(p[0], p[1], p[2]);
|
|
246
|
+
remote.targetYaw = yaw;
|
|
247
|
+
if (typeof expression === 'string')
|
|
248
|
+
setAvatarExpression(remote.root, expression);
|
|
249
|
+
if (label !== remote.label) {
|
|
250
|
+
remote.label = label;
|
|
251
|
+
disposeObject(remote.tag);
|
|
252
|
+
remote.tag = createNameTag(label);
|
|
253
|
+
remote.tag.position.y = TAG_HEIGHT;
|
|
254
|
+
remote.root.add(remote.tag);
|
|
255
|
+
}
|
|
256
|
+
// A long jump is a teleport or respawn, not a sprint: do not animate it.
|
|
257
|
+
if (remote.root.position.distanceTo(remote.target) > 6) {
|
|
258
|
+
snap = true;
|
|
259
|
+
remote.appear = 0;
|
|
260
|
+
setFigureOpacity(remote.root, 0);
|
|
261
|
+
}
|
|
262
|
+
if (snap) {
|
|
263
|
+
remote.root.position.copy(remote.target);
|
|
264
|
+
remote.yaw = yaw;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
clearRemotes() {
|
|
268
|
+
for (const remote of this.remotes.values())
|
|
269
|
+
disposeObject(remote.root);
|
|
270
|
+
this.remotes.clear();
|
|
271
|
+
}
|
|
272
|
+
// Leave the room promptly when the tab navigates away (e.g. through a door),
|
|
273
|
+
// and rejoin if the browser restores the page from its back/forward cache.
|
|
274
|
+
handlePageHide = () => {
|
|
275
|
+
window.clearTimeout(this.reconnectTimer);
|
|
276
|
+
this.disconnect();
|
|
277
|
+
};
|
|
278
|
+
handlePageShow = (event) => {
|
|
279
|
+
if (event.persisted && !this.closed) {
|
|
280
|
+
this.backoff = 1000;
|
|
281
|
+
this.connect();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function round(value) {
|
|
286
|
+
return Math.round(value * 100) / 100;
|
|
287
|
+
}
|
|
288
|
+
/** How long a newly appeared figure takes to go from transparent to opaque. */
|
|
289
|
+
const APPEAR_FADE = 0.55;
|
|
290
|
+
const GUEST = 'Guest';
|
|
291
|
+
/** Ease every mesh on an avatar toward a shared opacity. */
|
|
292
|
+
function setFigureOpacity(root, amount) {
|
|
293
|
+
const solid = amount >= 0.999;
|
|
294
|
+
let outlined = false;
|
|
295
|
+
root.traverse((child) => {
|
|
296
|
+
if (child instanceof Mesh && child.userData.stroke === true)
|
|
297
|
+
outlined = true;
|
|
298
|
+
});
|
|
299
|
+
root.traverse((child) => {
|
|
300
|
+
if (!(child instanceof Mesh))
|
|
301
|
+
return;
|
|
302
|
+
for (const material of Array.isArray(child.material) ? child.material : [child.material]) {
|
|
303
|
+
const stroke = material.userData.stroke === true;
|
|
304
|
+
const face = material instanceof MeshBasicMaterial && !stroke;
|
|
305
|
+
material.transparent = face || !solid;
|
|
306
|
+
material.opacity = amount;
|
|
307
|
+
// An outline sits behind the body, so a fading body still writes depth
|
|
308
|
+
// to keep the outline's far side from showing through it as a black blob.
|
|
309
|
+
material.depthWrite = stroke ? solid : !face && (solid || outlined);
|
|
310
|
+
material.needsUpdate = true;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
for (const child of root.children) {
|
|
314
|
+
if (!(child instanceof Sprite))
|
|
315
|
+
continue;
|
|
316
|
+
child.material.opacity = amount;
|
|
317
|
+
child.material.transparent = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* '@username' for a signed-in visitor, their made-up name in private mode,
|
|
322
|
+
* or 'Guest'. Only real usernames get the '@'.
|
|
323
|
+
*/
|
|
324
|
+
function nameLabel(name, alias) {
|
|
325
|
+
if (typeof name === 'string' && name)
|
|
326
|
+
return `@${name}`;
|
|
327
|
+
if (typeof alias === 'string' && alias)
|
|
328
|
+
return alias;
|
|
329
|
+
return GUEST;
|
|
330
|
+
}
|
|
331
|
+
/** A camera-facing label over a remote avatar. Guests are drawn dimmer. */
|
|
332
|
+
const BUBBLE_LIFE = 7;
|
|
333
|
+
/** Stick a line of text over a remote figure. The next line replaces it. */
|
|
334
|
+
function showBubble(remote, text) {
|
|
335
|
+
dropBubble(remote);
|
|
336
|
+
const bubble = createSpeechBubble(text);
|
|
337
|
+
bubble.position.y = TAG_HEIGHT + 0.42;
|
|
338
|
+
remote.root.add(bubble);
|
|
339
|
+
remote.bubble = bubble;
|
|
340
|
+
remote.bubbleLeft = BUBBLE_LIFE;
|
|
341
|
+
}
|
|
342
|
+
function dropBubble(remote) {
|
|
343
|
+
const bubble = remote.bubble;
|
|
344
|
+
if (!bubble)
|
|
345
|
+
return;
|
|
346
|
+
bubble.material.map?.dispose();
|
|
347
|
+
bubble.material.dispose();
|
|
348
|
+
bubble.removeFromParent();
|
|
349
|
+
remote.bubble = null;
|
|
350
|
+
}
|
|
351
|
+
/** A white speech bubble. One line, cut off if they wrote a novel. */
|
|
352
|
+
function createSpeechBubble(text) {
|
|
353
|
+
const scale = 2;
|
|
354
|
+
const font = `600 ${20 * scale}px system-ui, -apple-system, sans-serif`;
|
|
355
|
+
const canvas = document.createElement('canvas');
|
|
356
|
+
const context = canvas.getContext('2d');
|
|
357
|
+
context.font = font;
|
|
358
|
+
const padX = 16 * scale;
|
|
359
|
+
const height = 40 * scale;
|
|
360
|
+
const maxWidth = 280 * scale;
|
|
361
|
+
const measured = context.measureText(text).width;
|
|
362
|
+
canvas.width = Math.ceil(Math.min(maxWidth, measured) + padX * 2);
|
|
363
|
+
canvas.height = height;
|
|
364
|
+
context.font = font;
|
|
365
|
+
context.fillStyle = '#ffffff';
|
|
366
|
+
context.beginPath();
|
|
367
|
+
context.roundRect(0, 0, canvas.width, height, 14 * scale);
|
|
368
|
+
context.fill();
|
|
369
|
+
context.fillStyle = '#111111';
|
|
370
|
+
context.textAlign = 'center';
|
|
371
|
+
context.textBaseline = 'middle';
|
|
372
|
+
context.fillText(text, canvas.width / 2, height / 2, maxWidth);
|
|
373
|
+
const texture = new CanvasTexture(canvas);
|
|
374
|
+
const sprite = new Sprite(new SpriteMaterial({ map: texture, depthWrite: false, transparent: true }));
|
|
375
|
+
const worldHeight = 0.34;
|
|
376
|
+
sprite.scale.set((worldHeight * canvas.width) / height, worldHeight, 1);
|
|
377
|
+
sprite.name = 'worldmesh:speech';
|
|
378
|
+
sprite.center.set(0.5, 0);
|
|
379
|
+
return sprite;
|
|
380
|
+
}
|
|
381
|
+
function createNameTag(text) {
|
|
382
|
+
const scale = 2; // Canvas pixels per CSS pixel, for crisp text.
|
|
383
|
+
const font = `600 ${22 * scale}px system-ui, -apple-system, sans-serif`;
|
|
384
|
+
const canvas = document.createElement('canvas');
|
|
385
|
+
const context = canvas.getContext('2d');
|
|
386
|
+
context.font = font;
|
|
387
|
+
const padX = 12 * scale;
|
|
388
|
+
const height = 36 * scale;
|
|
389
|
+
canvas.width = Math.ceil(context.measureText(text).width + padX * 2);
|
|
390
|
+
canvas.height = height;
|
|
391
|
+
context.font = font;
|
|
392
|
+
context.fillStyle = 'rgba(0, 0, 0, 0.55)';
|
|
393
|
+
context.beginPath();
|
|
394
|
+
context.roundRect(0, 0, canvas.width, height, height / 2);
|
|
395
|
+
context.fill();
|
|
396
|
+
context.fillStyle = text === GUEST ? 'rgba(255, 255, 255, 0.7)' : '#ffffff';
|
|
397
|
+
context.textAlign = 'center';
|
|
398
|
+
context.textBaseline = 'middle';
|
|
399
|
+
context.fillText(text, canvas.width / 2, height / 2 + scale);
|
|
400
|
+
const texture = new CanvasTexture(canvas);
|
|
401
|
+
texture.anisotropy = 4;
|
|
402
|
+
const sprite = new Sprite(new SpriteMaterial({ map: texture, depthWrite: false, transparent: true }));
|
|
403
|
+
const worldHeight = 0.26;
|
|
404
|
+
sprite.scale.set((worldHeight * canvas.width) / height, worldHeight, 1);
|
|
405
|
+
sprite.name = 'worldmesh:name-tag';
|
|
406
|
+
return sprite;
|
|
407
|
+
}
|
|
408
|
+
function disposeObject(root) {
|
|
409
|
+
root.traverse((child) => {
|
|
410
|
+
if (child instanceof Sprite) {
|
|
411
|
+
child.material.map?.dispose();
|
|
412
|
+
child.material.dispose();
|
|
413
|
+
}
|
|
414
|
+
else if (child instanceof Mesh) {
|
|
415
|
+
child.geometry.dispose();
|
|
416
|
+
const material = child.material;
|
|
417
|
+
if (Array.isArray(material))
|
|
418
|
+
material.forEach((m) => m.dispose());
|
|
419
|
+
else
|
|
420
|
+
material.dispose();
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
root.removeFromParent();
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=presence.js.map
|