Commit 0962c57
committed
feat: consolidate player tracking in @dcl/sdk/players
The players helper gated arrivals on PlayerIdentityData AND AvatarBase, so a peer
whose avatar profile replicates late — or never — was never reported at all. Every
scene running an authoritative server therefore hand-rolled its own tracker:
diff PlayerIdentityData each frame, keep a Set of lowercased addresses, and poll
AvatarBase on a timer for the display name.
Rather than add a parallel API for that, this reworks the existing one.
onEnterScene/onLeaveScene keep their exact semantics and payloads by default, and
take an options object:
onEnterScene(cb) // unchanged: identity + profile
onEnterScene(cb, { requireProfile: false }) // as soon as the identity exists
Both thresholds come from one diff pass, so the sets cannot drift. Also new:
onPlayerNameChanged (replaces polling AvatarBase), getPlayers, getPlayerCount, a
never-empty displayName on the player payload, and nameResolved / joinedAtMs.
onLeaveScene gained a second argument carrying the last known state, since
getPlayer returns null once the entity is gone; extra callback parameters are
source-compatible, so existing one-argument handlers are unaffected.
Fixes found while reworking it, all of which affected client scenes too:
- `players.length === playerEntities.size` compared a count of entities against a
map cleaned elsewhere; the two could coincide while the sets differed, silently
dropping a join.
- Leaves were detected through AvatarBase.onChange, so a peer that lost identity
without a profile change never fired onLeaveScene — and the per-entity onChange
callbacks had no removal path, accumulating for the life of the process.
- Duplicate entities for one address resolved the name from whichever came first
in insertion order, pinning the player to the stale entity forever. Now the one
carrying a profile wins.
- A name starting with `0x` was treated as an address echo; `0xSomeName` is a
legal claimed name. Compared against the actual address instead.
- isGuest was captured once and never refreshed.
- getPlayer compared addresses case-sensitively, so a lowercased address silently
returned null.
- Callbacks had no unsubscribe and no error isolation: one throwing handler killed
the tracker for the rest of the run.
- The component rows and the callback lists are iterated over snapshots, and all
state settles before any callback runs, so handlers observe a consistent set even
when someone joins and someone else leaves on the same tick.
definePlayerHelper is now memoized per engine. It was being called twice with the
global engine — once here, once inside addSyncTransport — so two systems were
scanning the same component every frame; now there is one.
Snapshot goldens regenerated: the rewrite moves SCENE_COMPILED_JS_SIZE_PROD by
roughly +2.6KB on every scene bundle, since observables.ts pulls this module into
all of them.1 parent 5ffe873 commit 0962c57
16 files changed
Lines changed: 1704 additions & 117 deletions
File tree
- packages/@dcl/sdk/src
- network
- players
- test
- sdk
- snapshots
- development-bundles
- production-bundles
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
192 | 209 | | |
193 | 210 | | |
194 | 211 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | | - | |
| 11 | + | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
0 commit comments