@dcl/sdk 7.29.1-36637355925.commit-d261ba1 → 7.30.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/explorer-ui/index.d.ts +118 -0
- package/explorer-ui/index.js +235 -0
- package/internal/transports/rendererTransport.js +2 -5
- package/network/binary-message-bus.d.ts +3 -6
- package/network/binary-message-bus.js +5 -9
- package/network/index.d.ts +2 -8
- package/network/index.js +3 -16
- package/network/message-bus-sync.d.ts +3 -16
- package/network/message-bus-sync.js +103 -164
- package/network/state.js +5 -3
- package/package.json +6 -6
- package/src/explorer-ui/index.ts +398 -0
- package/src/internal/transports/rendererTransport.ts +1 -4
- package/src/network/binary-message-bus.ts +4 -9
- package/src/network/index.ts +3 -40
- package/src/network/message-bus-sync.ts +112 -180
- package/src/network/state.ts +4 -3
- package/atom.d.ts +0 -19
- package/atom.js +0 -83
- package/future.d.ts +0 -8
- package/future.js +0 -26
- package/network/chunking.d.ts +0 -5
- package/network/chunking.js +0 -38
- package/network/events/implementation.d.ts +0 -93
- package/network/events/implementation.js +0 -221
- package/network/events/index.d.ts +0 -42
- package/network/events/index.js +0 -43
- package/network/events/protocol.d.ts +0 -27
- package/network/events/protocol.js +0 -66
- package/network/events/registry.d.ts +0 -8
- package/network/events/registry.js +0 -3
- package/network/server/index.d.ts +0 -14
- package/network/server/index.js +0 -256
- package/network/server/utils.d.ts +0 -18
- package/network/server/utils.js +0 -135
- package/server/env-var.d.ts +0 -15
- package/server/env-var.js +0 -31
- package/server/index.d.ts +0 -2
- package/server/index.js +0 -3
- package/server/storage/constants.d.ts +0 -80
- package/server/storage/constants.js +0 -19
- package/server/storage/error-message.d.ts +0 -1
- package/server/storage/error-message.js +0 -9
- package/server/storage/index.d.ts +0 -54
- package/server/storage/index.js +0 -60
- package/server/storage/player.d.ts +0 -60
- package/server/storage/player.js +0 -244
- package/server/storage/scene.d.ts +0 -55
- package/server/storage/scene.js +0 -232
- package/server/storage/serialize.d.ts +0 -1
- package/server/storage/serialize.js +0 -135
- package/server/storage/value-cache.d.ts +0 -1
- package/server/storage/value-cache.js +0 -67
- package/server/storage/write-queue.d.ts +0 -1
- package/server/storage/write-queue.js +0 -113
- package/server/storage-url.d.ts +0 -13
- package/server/storage-url.js +0 -42
- package/server/utils.d.ts +0 -35
- package/server/utils.js +0 -69
- package/src/atom.ts +0 -98
- package/src/future.ts +0 -38
- package/src/network/chunking.ts +0 -45
- package/src/network/events/implementation.ts +0 -271
- package/src/network/events/index.ts +0 -48
- package/src/network/events/protocol.ts +0 -94
- package/src/network/events/registry.ts +0 -18
- package/src/network/server/index.ts +0 -341
- package/src/network/server/utils.ts +0 -189
- package/src/server/env-var.ts +0 -36
- package/src/server/index.ts +0 -12
- package/src/server/storage/constants.ts +0 -112
- package/src/server/storage/error-message.ts +0 -8
- package/src/server/storage/index.ts +0 -83
- package/src/server/storage/player.ts +0 -345
- package/src/server/storage/scene.ts +0 -324
- package/src/server/storage/serialize.ts +0 -132
- package/src/server/storage/value-cache.ts +0 -119
- package/src/server/storage/write-queue.ts +0 -174
- package/src/server/storage-url.ts +0 -49
- package/src/server/utils.ts +0 -83
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import {
|
|
2
|
+
engine,
|
|
3
|
+
ExplorerItemPurchaseResult,
|
|
4
|
+
ExplorerUi,
|
|
5
|
+
ExplorerUiEventsResult,
|
|
6
|
+
GrowOnlyValueSetComponentDefinition,
|
|
7
|
+
IEngine,
|
|
8
|
+
PBExplorerItemPurchaseResult,
|
|
9
|
+
PBExplorerUiEventsResult
|
|
10
|
+
} from '@dcl/ecs'
|
|
11
|
+
import { openExplorerUi, OpenExplorerUiResult } from '~system/RestrictedActions'
|
|
12
|
+
|
|
13
|
+
export { ExplorerUi, OpenExplorerUiResult }
|
|
14
|
+
|
|
15
|
+
// What an explorer that does not echo request_id, or a user-initiated action, leaves on the wire.
|
|
16
|
+
const UNCORRELATED = 0
|
|
17
|
+
|
|
18
|
+
// Panels that can be on screen next to another panel. The `ui` field alone cannot attribute
|
|
19
|
+
// their events, so an event on one of these panels must carry a request_id.
|
|
20
|
+
const CONCURRENT_PANELS: readonly ExplorerUi[] = [ExplorerUi.EU_ITEM_PURCHASE]
|
|
21
|
+
|
|
22
|
+
/** @public */
|
|
23
|
+
export type CorrelatedEvent = {
|
|
24
|
+
timestamp: number
|
|
25
|
+
requestId: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A named stream of events that this helper can collect and stop on.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export type Channel<N extends string = string, T extends CorrelatedEvent = CorrelatedEvent> = {
|
|
33
|
+
readonly name: N
|
|
34
|
+
readonly component: GrowOnlyValueSetComponentDefinition<T>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @public */
|
|
38
|
+
export type AnyChannel = Channel<string, any>
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Wraps a grow-only result component as a {@link Channel}. The name must be unique within a scene.
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export function channel<N extends string, T extends CorrelatedEvent>(
|
|
45
|
+
name: N,
|
|
46
|
+
component: GrowOnlyValueSetComponentDefinition<T>
|
|
47
|
+
): Channel<N, T> {
|
|
48
|
+
return { name, component }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Panel lifecycle events: opened and closed. @public */
|
|
52
|
+
export const ExplorerUiEvents: Channel<'explorerUi', PBExplorerUiEventsResult> = /* @__PURE__ */ channel(
|
|
53
|
+
'explorerUi',
|
|
54
|
+
ExplorerUiEventsResult
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
/** Outcome of an `EU_ITEM_PURCHASE` flow: purchased, dismissed or failed. @public */
|
|
58
|
+
export const ItemPurchase: Channel<'itemPurchase', PBExplorerItemPurchaseResult> = /* @__PURE__ */ channel(
|
|
59
|
+
'itemPurchase',
|
|
60
|
+
ExplorerItemPurchaseResult
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The `$case` names of a channel's event union.
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export type CaseOf<C extends AnyChannel> =
|
|
68
|
+
C extends Channel<any, infer T> ? Extract<T[keyof T], { $case: string }>['$case'] : never
|
|
69
|
+
|
|
70
|
+
/** @public */
|
|
71
|
+
export type Variant<C extends AnyChannel = AnyChannel, K extends string = string> = {
|
|
72
|
+
readonly channel: C
|
|
73
|
+
readonly $case: K
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @public */
|
|
77
|
+
export function variant<C extends AnyChannel, K extends CaseOf<C>>(ch: C, $case: K): Variant<C, K> {
|
|
78
|
+
return { channel: ch, $case }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What ends the wait before the panel closes: any event on a channel, or one `$case` of it.
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
export type StopCondition = AnyChannel | Variant
|
|
86
|
+
|
|
87
|
+
/** @public */
|
|
88
|
+
export type Collected<C> = C extends Channel<infer N, infer T> ? { channel: N; event: T } : never
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* How the wait ended.
|
|
92
|
+
* - `closed`: the panel this call opened was closed.
|
|
93
|
+
* - `matched`: `until` fired first, so the panel may still be on screen.
|
|
94
|
+
* - `notOpened`: this call did not open anything. `openResult` says why.
|
|
95
|
+
* - `timedOut`: `timeoutMs` elapsed first.
|
|
96
|
+
*
|
|
97
|
+
* `events` always holds everything collected, the event that stopped the wait included.
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
export type WaitOutcome<C extends readonly AnyChannel[]> =
|
|
101
|
+
| { $case: 'closed'; events: readonly Collected<C[number]>[] }
|
|
102
|
+
| { $case: 'matched'; events: readonly Collected<C[number]>[] }
|
|
103
|
+
| { $case: 'notOpened'; openResult: OpenExplorerUiResult }
|
|
104
|
+
| { $case: 'timedOut'; events: readonly Collected<C[number]>[] }
|
|
105
|
+
|
|
106
|
+
/** @public */
|
|
107
|
+
export type WaitOptions<C extends readonly AnyChannel[]> = {
|
|
108
|
+
/** Channels whose events land in `events`. Defaults to none. */
|
|
109
|
+
collect?: C
|
|
110
|
+
/** Stops the wait early. The panel closing always stops it regardless. */
|
|
111
|
+
until?: StopCondition
|
|
112
|
+
/**
|
|
113
|
+
* Bounds the wait, in milliseconds. On expiry the promise resolves with `$case: 'timedOut'`.
|
|
114
|
+
* No default, because a panel can stay open for minutes. Without it, an explorer that opens
|
|
115
|
+
* a panel and then emits no `closed` leaves the promise pending for the scene's whole life.
|
|
116
|
+
*/
|
|
117
|
+
timeoutMs?: number
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @public */
|
|
121
|
+
export type OpenExplorerUiRequest =
|
|
122
|
+
| { ui: Exclude<ExplorerUi, ExplorerUi.EU_ITEM_PURCHASE>; itemPurchase?: undefined }
|
|
123
|
+
| { ui: ExplorerUi.EU_ITEM_PURCHASE; itemPurchase: { urn: string } }
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Signature of {@link openExplorerUiAndWait}. The second overload takes a bare `ui`, so a
|
|
127
|
+
* computed panel value needs no narrowing.
|
|
128
|
+
* @public
|
|
129
|
+
*/
|
|
130
|
+
export interface OpenExplorerUiAndWait {
|
|
131
|
+
<const C extends readonly AnyChannel[] = []>(
|
|
132
|
+
request: OpenExplorerUiRequest,
|
|
133
|
+
options?: WaitOptions<C>
|
|
134
|
+
): Promise<WaitOutcome<C>>
|
|
135
|
+
(ui: Exclude<ExplorerUi, ExplorerUi.EU_ITEM_PURCHASE>): Promise<WaitOutcome<[]>>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @internal exposed for tests */
|
|
139
|
+
export const EXPLORER_UI_WAIT_TIMEOUT_SYSTEM = 'explorer-ui-wait-timeout'
|
|
140
|
+
|
|
141
|
+
// Lowest priority sorts this system last, so when it removes itself mid-tick it splices the
|
|
142
|
+
// last element of the array the engine is iterating and skips no other system.
|
|
143
|
+
const TIMEOUT_SYSTEM_PRIORITY = Number.MIN_SAFE_INTEGER
|
|
144
|
+
|
|
145
|
+
type OpenExplorerUiFn = (body: {
|
|
146
|
+
ui: ExplorerUi
|
|
147
|
+
requestId?: number
|
|
148
|
+
itemPurchase?: { urn: string }
|
|
149
|
+
}) => Promise<{ openResult: OpenExplorerUiResult }>
|
|
150
|
+
|
|
151
|
+
type CollectedEvent = { channel: string; event: CorrelatedEvent }
|
|
152
|
+
|
|
153
|
+
type Session = {
|
|
154
|
+
requestId: number
|
|
155
|
+
ui: ExplorerUi
|
|
156
|
+
correlationRequired: boolean
|
|
157
|
+
collect: ReadonlySet<string>
|
|
158
|
+
watched: ReadonlySet<string>
|
|
159
|
+
until: { channel: string; $case: string | undefined } | undefined
|
|
160
|
+
events: CollectedEvent[]
|
|
161
|
+
timeoutMs: number | undefined
|
|
162
|
+
elapsedMs: number
|
|
163
|
+
settled: boolean
|
|
164
|
+
settle: (outcome: WaitOutcome<never[]>) => void
|
|
165
|
+
fail: (error: Error) => void
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
type Attribution = 'yes' | 'no' | 'ambiguous'
|
|
169
|
+
|
|
170
|
+
// ts-proto renders a `oneof` as one wrapper property `{ $case, [$case]: value }`. Finding that
|
|
171
|
+
// property by shape keeps the helper independent of the name each message gave it: `event` on
|
|
172
|
+
// one message, `status` on another.
|
|
173
|
+
function caseOf(event: CorrelatedEvent): string | undefined {
|
|
174
|
+
for (const key of Object.keys(event)) {
|
|
175
|
+
const value = (event as unknown as Record<string, unknown>)[key]
|
|
176
|
+
if (value && typeof value === 'object' && typeof (value as { $case?: unknown }).$case === 'string') {
|
|
177
|
+
return (value as { $case: string }).$case
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return undefined
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeStop(stop: StopCondition): { channel: AnyChannel; $case: string | undefined } {
|
|
184
|
+
return '$case' in stop ? { channel: stop.channel, $case: stop.$case } : { channel: stop, $case: undefined }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** @internal test seam. The production instance is {@link openExplorerUiAndWait}. */
|
|
188
|
+
export function create(deps: {
|
|
189
|
+
engine: IEngine
|
|
190
|
+
openExplorerUi: OpenExplorerUiFn
|
|
191
|
+
explorerUiEvents: Channel<'explorerUi', PBExplorerUiEventsResult>
|
|
192
|
+
}) {
|
|
193
|
+
const { engine: engineInstance, openExplorerUi: openExplorerUiFn, explorerUiEvents } = deps
|
|
194
|
+
const root = engineInstance.RootEntity
|
|
195
|
+
|
|
196
|
+
const sessions = new Map<number, Session>()
|
|
197
|
+
// Keyed by name, so two wrappers around the same component arm one listener, not two.
|
|
198
|
+
const armed = new Map<string, GrowOnlyValueSetComponentDefinition<any>>()
|
|
199
|
+
// Starts at 1 so a minted id never collides with UNCORRELATED.
|
|
200
|
+
let nextRequestId = 1
|
|
201
|
+
let timeoutSystemAdded = false
|
|
202
|
+
|
|
203
|
+
function arm(ch: AnyChannel) {
|
|
204
|
+
const bound = armed.get(ch.name)
|
|
205
|
+
if (bound) {
|
|
206
|
+
if (bound !== ch.component) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`openExplorerUiAndWait: channel name '${ch.name}' is already bound to a different component. ` +
|
|
209
|
+
`Channel names identify a stream, so they have to be unique within a scene.`
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
armed.set(ch.name, ch.component)
|
|
215
|
+
const name = ch.name
|
|
216
|
+
// onChange has no unsubscribe, so one listener per channel lives for the engine's lifetime.
|
|
217
|
+
ch.component.onChange(root, (value: CorrelatedEvent | undefined) => {
|
|
218
|
+
// `undefined` arrives on DELETE_ENTITY. Otherwise `value` is one appended element,
|
|
219
|
+
// not the whole set.
|
|
220
|
+
if (value) deliver(name, value)
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function attribute(session: Session, name: string, event: CorrelatedEvent): Attribution {
|
|
225
|
+
if (event.requestId === session.requestId) return 'yes'
|
|
226
|
+
if (event.requestId !== UNCORRELATED) return 'no'
|
|
227
|
+
// Only the lifecycle channel carries a second discriminator, `ui`, and it identifies a
|
|
228
|
+
// session only while panels cannot coexist. A second call for the same panel is answered
|
|
229
|
+
// WAS_ALREADY_OPEN, so at most one session per non-concurrent panel is ever live.
|
|
230
|
+
if (session.correlationRequired || name !== explorerUiEvents.name) return 'ambiguous'
|
|
231
|
+
return (event as PBExplorerUiEventsResult).ui === session.ui ? 'yes' : 'no'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function deliver(name: string, event: CorrelatedEvent) {
|
|
235
|
+
// Settling deletes from `sessions` mid-iteration, which a Map tolerates. Nothing adds.
|
|
236
|
+
for (const session of sessions.values()) {
|
|
237
|
+
if (!session.watched.has(name)) continue
|
|
238
|
+
const attribution = attribute(session, name, event)
|
|
239
|
+
if (attribution === 'no') continue
|
|
240
|
+
if (attribution === 'ambiguous') {
|
|
241
|
+
failSession(
|
|
242
|
+
session,
|
|
243
|
+
new Error(
|
|
244
|
+
`openExplorerUiAndWait: got a '${name}' event with no request_id, and nothing else identifies ` +
|
|
245
|
+
`the call it belongs to. This explorer does not echo OpenExplorerUiRequest.request_id.`
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
accept(session, name, event)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function accept(session: Session, name: string, event: CorrelatedEvent) {
|
|
255
|
+
if (session.collect.has(name)) session.events.push({ channel: name, event })
|
|
256
|
+
|
|
257
|
+
// The close is terminal and wins over `until`, because `matched` would tell the caller
|
|
258
|
+
// that the panel is still on screen.
|
|
259
|
+
if (name === explorerUiEvents.name && caseOf(event) === 'closed') {
|
|
260
|
+
settleSession(session, { $case: 'closed', events: session.events as never[] })
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const until = session.until
|
|
265
|
+
if (!until || until.channel !== name) return
|
|
266
|
+
if (until.$case === undefined || until.$case === caseOf(event)) {
|
|
267
|
+
settleSession(session, { $case: 'matched', events: session.events as never[] })
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function settleSession(session: Session, outcome: WaitOutcome<never[]>) {
|
|
272
|
+
if (session.settled) return
|
|
273
|
+
session.settled = true
|
|
274
|
+
sessions.delete(session.requestId)
|
|
275
|
+
session.settle(outcome)
|
|
276
|
+
maybeRemoveTimeoutSystem()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function failSession(session: Session, error: Error) {
|
|
280
|
+
if (session.settled) return
|
|
281
|
+
session.settled = true
|
|
282
|
+
sessions.delete(session.requestId)
|
|
283
|
+
session.fail(error)
|
|
284
|
+
maybeRemoveTimeoutSystem()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function hasTimedSession(): boolean {
|
|
288
|
+
for (const session of sessions.values()) {
|
|
289
|
+
if (session.timeoutMs !== undefined) return true
|
|
290
|
+
}
|
|
291
|
+
return false
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function maybeRemoveTimeoutSystem() {
|
|
295
|
+
if (!timeoutSystemAdded || hasTimedSession()) return
|
|
296
|
+
timeoutSystemAdded = false
|
|
297
|
+
engineInstance.removeSystem(EXPLORER_UI_WAIT_TIMEOUT_SYSTEM)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function ensureTimeoutSystem() {
|
|
301
|
+
if (timeoutSystemAdded) return
|
|
302
|
+
timeoutSystemAdded = true
|
|
303
|
+
// The scene runtime has no timers, so elapsed time is accumulated from the engine's dt.
|
|
304
|
+
engineInstance.addSystem(
|
|
305
|
+
(dt: number) => {
|
|
306
|
+
for (const session of sessions.values()) {
|
|
307
|
+
if (session.timeoutMs === undefined) continue
|
|
308
|
+
session.elapsedMs += dt * 1000
|
|
309
|
+
if (session.elapsedMs >= session.timeoutMs) {
|
|
310
|
+
settleSession(session, { $case: 'timedOut', events: session.events as never[] })
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
maybeRemoveTimeoutSystem()
|
|
314
|
+
},
|
|
315
|
+
TIMEOUT_SYSTEM_PRIORITY,
|
|
316
|
+
EXPLORER_UI_WAIT_TIMEOUT_SYSTEM
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function openExplorerUiAndWait(
|
|
321
|
+
request: OpenExplorerUiRequest | Exclude<ExplorerUi, ExplorerUi.EU_ITEM_PURCHASE>,
|
|
322
|
+
options?: WaitOptions<readonly AnyChannel[]>
|
|
323
|
+
): Promise<WaitOutcome<never[]>> {
|
|
324
|
+
const { ui, itemPurchase } = typeof request === 'number' ? { ui: request, itemPurchase: undefined } : request
|
|
325
|
+
const requestId = nextRequestId++
|
|
326
|
+
|
|
327
|
+
const collect = options?.collect ?? []
|
|
328
|
+
const collectNames = new Set<string>()
|
|
329
|
+
for (const ch of collect) collectNames.add(ch.name)
|
|
330
|
+
|
|
331
|
+
const stop = options?.until ? normalizeStop(options.until) : undefined
|
|
332
|
+
const watched = new Set<string>(collectNames)
|
|
333
|
+
watched.add(explorerUiEvents.name)
|
|
334
|
+
if (stop) watched.add(stop.channel.name)
|
|
335
|
+
|
|
336
|
+
// Armed before the RPC, because events can land while the RPC is in flight. A listener
|
|
337
|
+
// that is already up is what makes replaying the accumulated set unnecessary.
|
|
338
|
+
arm(explorerUiEvents)
|
|
339
|
+
for (const ch of collect) arm(ch)
|
|
340
|
+
if (stop) arm(stop.channel)
|
|
341
|
+
|
|
342
|
+
let settle!: (outcome: WaitOutcome<never[]>) => void
|
|
343
|
+
let fail!: (error: Error) => void
|
|
344
|
+
const outcome = new Promise<WaitOutcome<never[]>>((resolve, reject) => {
|
|
345
|
+
settle = resolve
|
|
346
|
+
fail = reject
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
const session: Session = {
|
|
350
|
+
requestId,
|
|
351
|
+
ui,
|
|
352
|
+
correlationRequired: CONCURRENT_PANELS.includes(ui),
|
|
353
|
+
collect: collectNames,
|
|
354
|
+
watched,
|
|
355
|
+
until: stop ? { channel: stop.channel.name, $case: stop.$case } : undefined,
|
|
356
|
+
events: [],
|
|
357
|
+
timeoutMs: options?.timeoutMs,
|
|
358
|
+
elapsedMs: 0,
|
|
359
|
+
settled: false,
|
|
360
|
+
settle,
|
|
361
|
+
fail
|
|
362
|
+
}
|
|
363
|
+
sessions.set(requestId, session)
|
|
364
|
+
if (session.timeoutMs !== undefined) ensureTimeoutSystem()
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
const { openResult } = await openExplorerUiFn({ ui, requestId, itemPurchase })
|
|
368
|
+
// This call opened nothing, so no event carrying this request id is coming.
|
|
369
|
+
if (openResult !== OpenExplorerUiResult.OPENED) {
|
|
370
|
+
settleSession(session, { $case: 'notOpened', openResult })
|
|
371
|
+
}
|
|
372
|
+
} catch (error) {
|
|
373
|
+
// Routed through the session rather than rethrown, so that `outcome` is never left
|
|
374
|
+
// pending and unobserved.
|
|
375
|
+
failSession(session, error as Error)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return outcome
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// The implementation takes both argument forms and is blind to `collect`. The cast is what
|
|
382
|
+
// gives callers the two overloads that relate them.
|
|
383
|
+
return { openExplorerUiAndWait: openExplorerUiAndWait as OpenExplorerUiAndWait }
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const helper = /* @__PURE__ */ create({ engine, openExplorerUi, explorerUiEvents: ExplorerUiEvents })
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Opens an explorer panel and waits for the session it started. The wait ends when the panel
|
|
390
|
+
* closes. See {@link WaitOptions} to collect events, to stop earlier or to bound the wait.
|
|
391
|
+
*
|
|
392
|
+
* Every lifecycle outcome resolves, see {@link WaitOutcome}. The promise rejects only when an
|
|
393
|
+
* event cannot be attributed to a call at all: an explorer too old to echo `request_id`
|
|
394
|
+
* paired with a panel that can coexist with another one.
|
|
395
|
+
*
|
|
396
|
+
* @public
|
|
397
|
+
*/
|
|
398
|
+
export const openExplorerUiAndWait: OpenExplorerUiAndWait = helper.openExplorerUiAndWait
|
|
@@ -41,10 +41,7 @@ export function createRendererTransport(engineApi: EngineApiForTransport): Trans
|
|
|
41
41
|
}
|
|
42
42
|
return !!message
|
|
43
43
|
},
|
|
44
|
-
type: 'renderer'
|
|
45
|
-
// The renderer/host is the authority for reserved-range entities (avatars,
|
|
46
|
-
// root/player/camera): it is the only transport allowed to mutate or delete them.
|
|
47
|
-
allowReservedEntities: true
|
|
44
|
+
type: 'renderer'
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
return rendererTransport
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { ReadWriteByteBuffer } from '@dcl/ecs/dist/serialization/ByteBuffer'
|
|
2
2
|
|
|
3
3
|
export enum CommsMessage {
|
|
4
|
-
CRDT =
|
|
5
|
-
REQ_CRDT_STATE =
|
|
6
|
-
RES_CRDT_STATE =
|
|
7
|
-
CRDT_SERVER = 4,
|
|
8
|
-
CRDT_AUTHORITATIVE = 5,
|
|
9
|
-
CUSTOM_EVENT = 6
|
|
4
|
+
CRDT = 1,
|
|
5
|
+
REQ_CRDT_STATE = 2,
|
|
6
|
+
RES_CRDT_STATE = 3
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
export function BinaryMessageBus<T extends CommsMessage>(
|
|
@@ -23,9 +20,7 @@ export function BinaryMessageBus<T extends CommsMessage>(
|
|
|
23
20
|
__processMessages: (messages: Uint8Array[]) => {
|
|
24
21
|
for (const message of messages) {
|
|
25
22
|
const commsMsg = decodeCommsMessage<T>(message)
|
|
26
|
-
if (!commsMsg)
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
23
|
+
if (!commsMsg) continue
|
|
29
24
|
const { sender, messageType, data } = commsMsg
|
|
30
25
|
const fn = mapping.get(messageType)
|
|
31
26
|
if (fn) fn(data, sender)
|
package/src/network/index.ts
CHANGED
|
@@ -2,46 +2,9 @@ import { sendBinary } from '~system/CommunicationsController'
|
|
|
2
2
|
import { engine } from '@dcl/ecs'
|
|
3
3
|
import { addSyncTransport } from './message-bus-sync'
|
|
4
4
|
import { getUserData } from '~system/UserIdentity'
|
|
5
|
-
import { isServer as isServerApi } from '~system/EngineApi'
|
|
6
|
-
import { Atom } from '../atom'
|
|
7
|
-
|
|
8
|
-
// Create isServer atom for consistent state
|
|
9
|
-
const isServerAtom = Atom<boolean>(false)
|
|
10
|
-
void isServerApi({}).then((response) => {
|
|
11
|
-
isServerAtom.swap(!!response.isServer)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
// Helper function to check if running on server
|
|
15
|
-
export function isServer(): boolean {
|
|
16
|
-
return isServerAtom.getOrNull() ?? false
|
|
17
|
-
}
|
|
18
5
|
|
|
19
6
|
// initialize sync transport for sdk engine
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
syncEntity,
|
|
23
|
-
parentEntity,
|
|
24
|
-
getParent,
|
|
25
|
-
myProfile,
|
|
26
|
-
removeParent,
|
|
27
|
-
getFirstChild,
|
|
28
|
-
isStateSyncronized,
|
|
29
|
-
binaryMessageBus,
|
|
30
|
-
eventBus
|
|
31
|
-
} = addSyncTransport(engine, sendBinary, getUserData, isServerApi, 'network')
|
|
32
|
-
|
|
33
|
-
// Re-export the room messaging system
|
|
34
|
-
export { registerMessages, getRoom } from './events'
|
|
7
|
+
const { getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, getFirstChild, isStateSyncronized } =
|
|
8
|
+
addSyncTransport(engine, sendBinary, getUserData)
|
|
35
9
|
|
|
36
|
-
export {
|
|
37
|
-
getFirstChild,
|
|
38
|
-
getChildren,
|
|
39
|
-
syncEntity,
|
|
40
|
-
parentEntity,
|
|
41
|
-
getParent,
|
|
42
|
-
myProfile,
|
|
43
|
-
removeParent,
|
|
44
|
-
isStateSyncronized,
|
|
45
|
-
binaryMessageBus,
|
|
46
|
-
eventBus
|
|
47
|
-
}
|
|
10
|
+
export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, isStateSyncronized }
|