-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathindex.ts
More file actions
118 lines (105 loc) · 3.94 KB
/
Copy pathindex.ts
File metadata and controls
118 lines (105 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { Entity, IEngine, TransformType, engine } from '@dcl/ecs'
import {
PlayerIdentityData as definePlayerIdenityData,
AvatarBase as defineAvatarBase,
AvatarEquippedData as defineAvatarEquippedData,
PBAvatarBase,
PBAvatarEquippedData,
Transform as defineTransform
} from '@dcl/ecs/dist/components'
type GetPlayerDataReq = {
userId: string
}
type GetPlayerDataRes = {
entity: Entity
name: string
isGuest: boolean
userId: string
avatar?: PBAvatarBase
wearables: PBAvatarEquippedData['wearableUrns']
emotes: PBAvatarEquippedData['emoteUrns']
position: TransformType['position'] | undefined
}
export function definePlayerHelper(engine: IEngine) {
const Transform = defineTransform(engine)
const PlayerIdentityData = definePlayerIdenityData(engine)
const AvatarEquippedData = defineAvatarEquippedData(engine)
const AvatarBase = defineAvatarBase(engine)
const playerEntities = new Map<Entity, string>()
const onEnterSceneCb: ((player: GetPlayerDataRes) => void)[] = []
const onLeaveSceneCb: ((userId: string) => void)[] = []
// Both directions are polled. The `players.length === playerEntities.size`
// shortcut this replaces went blind on a frame where one player joined and
// another left, and the per-entity `AvatarBase.onChange` that used to report a
// departure never fired for a player whose entity was removed whole.
engine.addSystem(() => {
const present = new Set<Entity>()
for (const [entity, identity] of engine.getEntitiesWith(PlayerIdentityData, AvatarBase)) {
present.add(entity)
if (playerEntities.has(entity)) continue
playerEntities.set(entity, identity.address)
for (const cb of onEnterSceneCb) cb(getPlayer({ userId: identity.address })!)
}
for (const [entity, address] of playerEntities) {
if (present.has(entity)) continue
playerEntities.delete(entity)
for (const cb of onLeaveSceneCb) cb(address)
}
})
return {
onEnterScene(cb: (player: GetPlayerDataRes) => void) {
onEnterSceneCb.push(cb)
},
onLeaveScene(cb: (userId: string) => void) {
onLeaveSceneCb.push(cb)
},
/**
* Returns the info of the player if it's in the scene.
*/
getPlayer(user?: GetPlayerDataReq): GetPlayerDataRes | null {
function getEntity() {
if (!user?.userId) return engine.PlayerEntity
for (const [entity, data] of engine.getEntitiesWith(PlayerIdentityData)) {
if (data.address === user.userId) {
return entity
}
}
return undefined
}
const userEntity = getEntity()
if (!userEntity) return null
const playerData = PlayerIdentityData.getOrNull(userEntity)
const avatarData = AvatarBase.getOrNull(userEntity)
const wearablesData = AvatarEquippedData.getOrNull(userEntity)
if (!playerData && !avatarData && !wearablesData) return null
return {
entity: userEntity,
name: avatarData?.name ?? '',
isGuest: !!playerData?.isGuest,
userId: playerData?.address ?? '',
avatar: avatarData ?? undefined,
wearables: wearablesData?.wearableUrns ?? [],
emotes: wearablesData?.emoteUrns ?? [],
position: Transform.getOrNull(userEntity)?.position
}
}
}
}
type PlayerHelper = ReturnType<typeof definePlayerHelper>
const helpers = new WeakMap<IEngine, PlayerHelper>()
/**
* One helper per engine. Both the public API below and `addSyncTransport` need
* one, and two of them on the same engine means two identical per-frame diffs
* and every `onEnterScene` callback firing twice.
*/
export function getPlayerHelper(engine: IEngine): PlayerHelper {
const existing = helpers.get(engine)
if (existing) return existing
const helper = definePlayerHelper(engine)
helpers.set(engine, helper)
return helper
}
const players = getPlayerHelper(engine)
const { getPlayer, onEnterScene, onLeaveScene } = players
export { getPlayer, onEnterScene, onLeaveScene }
export default players