@dcl/sdk 7.29.1-36863326504.commit-aa54c50 → 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.
Files changed (80) hide show
  1. package/explorer-ui/index.d.ts +118 -0
  2. package/explorer-ui/index.js +235 -0
  3. package/internal/transports/rendererTransport.js +2 -5
  4. package/network/binary-message-bus.d.ts +3 -6
  5. package/network/binary-message-bus.js +5 -9
  6. package/network/index.d.ts +2 -8
  7. package/network/index.js +3 -16
  8. package/network/message-bus-sync.d.ts +3 -16
  9. package/network/message-bus-sync.js +103 -164
  10. package/network/state.js +5 -3
  11. package/package.json +6 -6
  12. package/src/explorer-ui/index.ts +398 -0
  13. package/src/internal/transports/rendererTransport.ts +1 -4
  14. package/src/network/binary-message-bus.ts +4 -9
  15. package/src/network/index.ts +3 -40
  16. package/src/network/message-bus-sync.ts +112 -180
  17. package/src/network/state.ts +4 -3
  18. package/atom.d.ts +0 -19
  19. package/atom.js +0 -83
  20. package/future.d.ts +0 -8
  21. package/future.js +0 -26
  22. package/network/chunking.d.ts +0 -5
  23. package/network/chunking.js +0 -38
  24. package/network/events/implementation.d.ts +0 -93
  25. package/network/events/implementation.js +0 -221
  26. package/network/events/index.d.ts +0 -42
  27. package/network/events/index.js +0 -43
  28. package/network/events/protocol.d.ts +0 -27
  29. package/network/events/protocol.js +0 -66
  30. package/network/events/registry.d.ts +0 -8
  31. package/network/events/registry.js +0 -3
  32. package/network/server/index.d.ts +0 -14
  33. package/network/server/index.js +0 -256
  34. package/network/server/utils.d.ts +0 -18
  35. package/network/server/utils.js +0 -135
  36. package/server/env-var.d.ts +0 -15
  37. package/server/env-var.js +0 -31
  38. package/server/index.d.ts +0 -2
  39. package/server/index.js +0 -3
  40. package/server/storage/constants.d.ts +0 -80
  41. package/server/storage/constants.js +0 -19
  42. package/server/storage/error-message.d.ts +0 -1
  43. package/server/storage/error-message.js +0 -9
  44. package/server/storage/index.d.ts +0 -54
  45. package/server/storage/index.js +0 -60
  46. package/server/storage/player.d.ts +0 -60
  47. package/server/storage/player.js +0 -244
  48. package/server/storage/scene.d.ts +0 -55
  49. package/server/storage/scene.js +0 -232
  50. package/server/storage/serialize.d.ts +0 -1
  51. package/server/storage/serialize.js +0 -135
  52. package/server/storage/value-cache.d.ts +0 -1
  53. package/server/storage/value-cache.js +0 -67
  54. package/server/storage/write-queue.d.ts +0 -1
  55. package/server/storage/write-queue.js +0 -113
  56. package/server/storage-url.d.ts +0 -13
  57. package/server/storage-url.js +0 -42
  58. package/server/utils.d.ts +0 -35
  59. package/server/utils.js +0 -69
  60. package/src/atom.ts +0 -98
  61. package/src/future.ts +0 -38
  62. package/src/network/chunking.ts +0 -45
  63. package/src/network/events/implementation.ts +0 -271
  64. package/src/network/events/index.ts +0 -48
  65. package/src/network/events/protocol.ts +0 -94
  66. package/src/network/events/registry.ts +0 -18
  67. package/src/network/server/index.ts +0 -341
  68. package/src/network/server/utils.ts +0 -189
  69. package/src/server/env-var.ts +0 -36
  70. package/src/server/index.ts +0 -12
  71. package/src/server/storage/constants.ts +0 -112
  72. package/src/server/storage/error-message.ts +0 -8
  73. package/src/server/storage/index.ts +0 -83
  74. package/src/server/storage/player.ts +0 -345
  75. package/src/server/storage/scene.ts +0 -324
  76. package/src/server/storage/serialize.ts +0 -132
  77. package/src/server/storage/value-cache.ts +0 -119
  78. package/src/server/storage/write-queue.ts +0 -174
  79. package/src/server/storage-url.ts +0 -49
  80. package/src/server/utils.ts +0 -83
@@ -1,341 +0,0 @@
1
- import {
2
- IEngine,
3
- Entity,
4
- CrdtMessageType,
5
- CrdtMessageBody,
6
- ProcessMessageResultType,
7
- ComponentType,
8
- PutNetworkComponentOperation
9
- } from '@dcl/ecs'
10
- import * as components from '@dcl/ecs/dist/components'
11
- import { ReadWriteByteBuffer } from '@dcl/ecs/dist/serialization/ByteBuffer'
12
- import { CommsMessage } from '../binary-message-bus'
13
- import { chunkCrdtMessages } from '../chunking'
14
- import * as utils from './utils'
15
- import { AUTH_SERVER_PEER_ID, DEBUG_NETWORK_MESSAGES } from '../message-bus-sync'
16
- import { type BinaryMessageBus } from '../binary-message-bus'
17
- import {
18
- LastWriteWinElementSetComponentDefinition,
19
- GrowOnlyValueSetComponentDefinition,
20
- ComponentDefinition,
21
- InternalBaseComponent
22
- } from '@dcl/ecs/dist/engine/component'
23
-
24
- export const LIVEKIT_MAX_SIZE = 12
25
-
26
- export interface ServerValidationConfig {
27
- engine: IEngine
28
- binaryMessageBus: ReturnType<typeof BinaryMessageBus>
29
- }
30
-
31
- export function createServerValidator(config: ServerValidationConfig) {
32
- const { engine, binaryMessageBus } = config
33
-
34
- // Initialize components for network operations and transform fixing
35
- const NetworkEntity = components.NetworkEntity(engine)
36
- const CreatedBy = components.CreatedBy(engine)
37
- const NetworkParent = components.NetworkParent(engine)
38
-
39
- // Type guard to check if component supports corrections (both LWW and GrowOnlySet)
40
- function supportsCorrections<T>(
41
- component: ComponentDefinition<T>
42
- ): component is LastWriteWinElementSetComponentDefinition<T> | GrowOnlyValueSetComponentDefinition<T> {
43
- return (
44
- (component.componentType === ComponentType.LastWriteWinElementSet ||
45
- component.componentType === ComponentType.GrowOnlyValueSet) &&
46
- 'getCrdtState' in component
47
- )
48
- }
49
-
50
- function findExistingNetworkEntity(message: utils.NetworkMessage): Entity | null {
51
- // Look for existing network entity mapping (don't create new ones)
52
- for (const [entityId, networkData] of engine.getEntitiesWith(NetworkEntity)) {
53
- if (networkData.networkId === message.networkId && networkData.entityId === message.entityId) {
54
- return entityId
55
- }
56
- }
57
- // Return null if not found
58
- return null
59
- }
60
-
61
- /**
62
- * What a message can be judged on before an entity is spent mapping it. Anything
63
- * needing the local entity — the CRDT dry run, the scene's own validator — still
64
- * happens in validateMessagePermissions afterwards.
65
- */
66
- function peerMessageIsEligible(message: utils.NetworkMessage): boolean {
67
- if (!('componentId' in message)) return true
68
-
69
- const definition = engine.getComponentOrNull(message.componentId)
70
- if (!definition) return false
71
-
72
- if ('data' in message) {
73
- try {
74
- definition.schema.deserialize(new ReadWriteByteBuffer(message.data))
75
- } catch {
76
- return false
77
- }
78
- }
79
-
80
- return true
81
- }
82
-
83
- function findOrCreateNetworkEntity(message: utils.NetworkMessage, sender: string, isServer: boolean): Entity {
84
- // Look for existing network entity mapping first
85
- const existingEntity = findExistingNetworkEntity(message)
86
-
87
- if (existingEntity) {
88
- return existingEntity
89
- }
90
-
91
- // Create new entity and network mapping
92
- const newEntityId = engine.addEntity()
93
- NetworkEntity.createOrReplace(newEntityId, {
94
- networkId: message.networkId,
95
- entityId: message.entityId
96
- })
97
-
98
- if (isServer) {
99
- CreatedBy.createOrReplace(newEntityId, { address: sender })
100
- }
101
-
102
- DEBUG_NETWORK_MESSAGES() &&
103
- console.log(`[DEBUG] Created new entity ${newEntityId} for network ${message.networkId}:${message.entityId}`)
104
- return newEntityId
105
- }
106
-
107
- function convertNetworkToRegularMessage(
108
- networkMessage: utils.NetworkMessage,
109
- localEntityId: Entity,
110
- forceCorrections = false
111
- ): (CrdtMessageBody & { messageBuffer: Uint8Array }) | null {
112
- const buffer = new ReadWriteByteBuffer()
113
-
114
- try {
115
- // Use the well-tested networkMessageToLocal utility with transform fixing for Unity
116
- const message = utils.networkMessageToLocal(
117
- networkMessage,
118
- localEntityId,
119
- buffer,
120
- NetworkParent,
121
- forceCorrections
122
- )
123
- return { ...message, messageBuffer: buffer.toBinary() }
124
- } catch (error) {
125
- DEBUG_NETWORK_MESSAGES() && console.error('Error converting network message:', error)
126
- return null
127
- }
128
- }
129
-
130
- function validateMessagePermissions(message: utils.RegularMessage, sender: string, _localEntityId: Entity): boolean {
131
- // Basic checks
132
- if (!sender || sender === AUTH_SERVER_PEER_ID) {
133
- return false // Server shouldn't send messages to itself
134
- }
135
-
136
- if (message.type === CrdtMessageType.DELETE_ENTITY) {
137
- // TODO: how to handle this case ?
138
- }
139
-
140
- if (message.type === CrdtMessageType.PUT_COMPONENT || message.type === CrdtMessageType.DELETE_COMPONENT) {
141
- const component = engine.getComponentOrNull(message.componentId) as InternalBaseComponent<unknown> | null
142
- if (!component) return false
143
-
144
- let value: unknown = null
145
- if ('data' in message) {
146
- try {
147
- value = component.schema.deserialize(new ReadWriteByteBuffer(message.data))
148
- } catch {
149
- return false
150
- }
151
- }
152
-
153
- const dryRunCRDT = component.__dry_run_updateFromCrdt(message)
154
- const validCRDT = [
155
- ProcessMessageResultType.StateUpdatedData,
156
- ProcessMessageResultType.StateUpdatedTimestamp,
157
- ProcessMessageResultType.EntityDeleted
158
- ].includes(dryRunCRDT)
159
- const createdBy = CreatedBy.getOrNull(message.entityId)
160
- const validMessage =
161
- validCRDT &&
162
- component.__run_validateBeforeChange(message.entityId, value, sender, createdBy?.address ?? AUTH_SERVER_PEER_ID)
163
-
164
- return !!validMessage
165
- }
166
-
167
- // For now, basic validation - in the future this will check component sync permissions
168
- // TODO: Check if sender owns the entity
169
- // TODO: Check component sync mode ('all' | 'owner' | 'server')
170
- // TODO: Run component custom validation
171
- return true
172
- }
173
-
174
- function broadcastBatchedMessages(messages: utils.NetworkMessage[], excludeSender: string) {
175
- if (messages.length === 0) return
176
-
177
- // Build the complete buffer with all messages
178
- const networkBuffer = new ReadWriteByteBuffer()
179
- for (const message of messages) {
180
- // Skip oversized messages upfront
181
- if (message.messageBuffer.byteLength / 1024 > LIVEKIT_MAX_SIZE) {
182
- console.error(
183
- `Message too large (${message.messageBuffer.byteLength} bytes), skipping message from ${excludeSender}`
184
- )
185
- continue
186
- }
187
- networkBuffer.writeBuffer(message.messageBuffer, false)
188
- }
189
-
190
- // Use the chunking function to split into proper chunks
191
- const chunks = chunkCrdtMessages(networkBuffer.toBinary(), LIVEKIT_MAX_SIZE)
192
-
193
- for (const chunk of chunks) {
194
- binaryMessageBus.emit(CommsMessage.CRDT, chunk)
195
- }
196
- DEBUG_NETWORK_MESSAGES() &&
197
- console.log(`Total: ${messages.length} messages in ${chunks.length} chunks from ${excludeSender}`)
198
- }
199
-
200
- function sendCorrectionToSender(networkMessage: utils.NetworkMessage, sender: string, localEntityId: Entity) {
201
- try {
202
- // Only handle component messages (PUT/DELETE), not entity deletion
203
- if (networkMessage.type === CrdtMessageType.DELETE_ENTITY_NETWORK) {
204
- DEBUG_NETWORK_MESSAGES() && console.log('[AUTHORITATIVE] Cannot send authoritative message for entity deletion')
205
- return
206
- }
207
-
208
- // Safe to access componentId and timestamp now
209
- const component = engine.getComponent(networkMessage.componentId)
210
-
211
- // Only proceed if component supports authoritative messages (LWW or GrowOnlySet)
212
- if (!supportsCorrections(component)) {
213
- DEBUG_NETWORK_MESSAGES() && console.log('[AUTHORITATIVE] Component does not support authoritative messages')
214
- return
215
- }
216
-
217
- const serverCRDTState = component.getCrdtState(localEntityId)
218
-
219
- if (serverCRDTState) {
220
- // Create authoritative message using PUT_COMPONENT_NETWORK
221
- // Each client will convert this to AUTHORITATIVE_PUT_COMPONENT with proper entity mapping
222
- const correctionBuffer = new ReadWriteByteBuffer()
223
- PutNetworkComponentOperation.write(
224
- networkMessage.entityId, // Use original network entity ID
225
- serverCRDTState.timestamp,
226
- networkMessage.componentId,
227
- networkMessage.networkId,
228
- serverCRDTState.data,
229
- correctionBuffer
230
- )
231
- // Send authoritative message directly to the sender
232
- binaryMessageBus.emit(CommsMessage.CRDT_AUTHORITATIVE, correctionBuffer.toBinary(), [sender])
233
-
234
- DEBUG_NETWORK_MESSAGES() &&
235
- console.log(
236
- `[AUTHORITATIVE] Sent authoritative message to ${sender} for entity ${localEntityId} component ${networkMessage.componentId} with timestamp ${networkMessage.timestamp}`
237
- )
238
- }
239
- } catch (error) {
240
- DEBUG_NETWORK_MESSAGES() && console.error('Error sending correction:', error)
241
- }
242
- }
243
-
244
- return {
245
- findExistingNetworkEntity,
246
- // transform Network messages to CRDT Common Messages.
247
- processClientMessages: function processClientMessages(value: Uint8Array, sender: string, forceCorrections = false) {
248
- // console.log(`[CLIENT] Processing message from ${sender}, ${value.length} bytes`)
249
-
250
- // Collect all regular messages in a single buffer for batched application
251
- const combinedBuffer = new ReadWriteByteBuffer()
252
-
253
- // Clients process network messages from server and convert them to regular messages
254
- for (const message of utils.readMessages(value)) {
255
- // Only process network messages in client message handler
256
- if (utils.isNetworkMessage(message)) {
257
- const networkMessage = message as utils.NetworkMessage
258
-
259
- // Find or create network entity mapping
260
- const localEntityId = findOrCreateNetworkEntity(networkMessage, sender, false)
261
-
262
- // Convert network message to regular message or correction message
263
- const regularMessage = convertNetworkToRegularMessage(networkMessage, localEntityId, forceCorrections)
264
-
265
- if (regularMessage?.messageBuffer.byteLength) {
266
- combinedBuffer.writeBuffer(regularMessage.messageBuffer, false)
267
- }
268
- }
269
- }
270
- return combinedBuffer.toBinary()
271
- },
272
- // Sever Code: process message, handle permissions, and broadcast if needed.
273
- processServerMessages: function processServerMessages(value: Uint8Array, sender: string) {
274
- // console.log(`[SERVER] Processing message from ${sender}, ${value.length} bytes`)
275
-
276
- // Collect all valid messages for batched broadcasting
277
- const messagesToBroadcast: utils.NetworkMessage[] = []
278
- const regularMessagesBuffer = new ReadWriteByteBuffer()
279
-
280
- for (const message of utils.readMessages(value)) {
281
- try {
282
- // Only process network messages in server message handler
283
- if (utils.isNetworkMessage(message)) {
284
- const networkMessage = message as utils.NetworkMessage
285
-
286
- // 0. Refuse what is already unusable before an entity is spent on it. A peer
287
- // sending unique (networkId, entityId) pairs it knows will be refused would
288
- // otherwise drain the range one refusal at a time. A pair already mapped takes
289
- // the normal path, so its sender still gets the correction it is owed.
290
- if (findExistingNetworkEntity(networkMessage) === null && !peerMessageIsEligible(networkMessage)) {
291
- continue
292
- }
293
-
294
- // 1. Find or create network entity mapping
295
- const localEntityId = findOrCreateNetworkEntity(networkMessage, sender, true)
296
-
297
- // 2. Convert network message to regular message and collect for local application
298
- const regularMessage = convertNetworkToRegularMessage(networkMessage, localEntityId)
299
-
300
- // 3. Basic permission validation.
301
- if (!regularMessage || !validateMessagePermissions(regularMessage as any, sender, localEntityId)) {
302
- // Send correction back to sender with server's authoritative state
303
- sendCorrectionToSender(networkMessage, sender, localEntityId)
304
- continue
305
- }
306
-
307
- // 4. Collect valid message for batched broadcasting
308
- messagesToBroadcast.push(networkMessage)
309
-
310
- if (regularMessage?.messageBuffer.byteLength) {
311
- regularMessagesBuffer.writeBuffer(regularMessage.messageBuffer, false)
312
- }
313
- }
314
- } catch (error) {
315
- DEBUG_NETWORK_MESSAGES() && console.error('Error processing server message:', error)
316
- }
317
- }
318
- // Batch broadcast all valid messages together
319
- broadcastBatchedMessages(messagesToBroadcast, sender)
320
- return regularMessagesBuffer.toBinary()
321
- },
322
- // engine changes that needs to be broadcasted.
323
- convertRegularToNetworkMessage: function convertRegularToNetworkMessage(regularMessage: Uint8Array): Uint8Array[] {
324
- const groupedBuffer = new ReadWriteByteBuffer()
325
-
326
- // First pass: Convert all regular messages to network format and group them into one big buffer
327
- for (const message of utils.readMessages(regularMessage)) {
328
- // Only convert regular messages that have network data
329
- const networkData = NetworkEntity.getOrNull(message.entityId)
330
-
331
- if (networkData && !utils.isNetworkMessage(message)) {
332
- utils.localMessageToNetwork(message, networkData, groupedBuffer)
333
- }
334
- }
335
-
336
- // Second pass: Use the new chunking function that respects message boundaries
337
- const totalData = groupedBuffer.toBinary()
338
- return chunkCrdtMessages(totalData, LIVEKIT_MAX_SIZE)
339
- }
340
- }
341
- }
@@ -1,189 +0,0 @@
1
- import { Entity } from '@dcl/ecs/dist/engine'
2
- import { CrdtMessageProtocol, NetworkParent } from '@dcl/ecs'
3
- import { ReceiveMessage } from '@dcl/ecs/dist/runtime/types'
4
- import { ReceiveNetworkMessage } from '@dcl/ecs/dist/systems/crdt/types'
5
- import { ByteBuffer, ReadWriteByteBuffer } from '@dcl/ecs/dist/serialization/ByteBuffer'
6
- import { AuthoritativePutComponentOperation, PutComponentOperation } from '@dcl/ecs/dist/serialization/crdt'
7
- import {
8
- CrdtMessage,
9
- CrdtMessageBody,
10
- CrdtMessageHeader,
11
- CrdtMessageType,
12
- DeleteComponentMessage,
13
- DeleteComponentNetworkMessage,
14
- DeleteEntityMessage,
15
- DeleteEntityNetworkMessage,
16
- PutComponentMessage,
17
- AuthoritativePutComponentMessage,
18
- PutNetworkComponentMessage
19
- } from '@dcl/ecs/dist/serialization/crdt/types'
20
- import { DeleteComponent } from '@dcl/ecs/dist/serialization/crdt/deleteComponent'
21
- import { DeleteEntity } from '@dcl/ecs/dist/serialization/crdt/deleteEntity'
22
- import { INetowrkEntityType } from '@dcl/ecs/dist/components/types'
23
- import { PutNetworkComponentOperation } from '@dcl/ecs/dist/serialization/crdt/network/putComponentNetwork'
24
- import { DeleteComponentNetwork } from '@dcl/ecs/dist/serialization/crdt/network/deleteComponentNetwork'
25
- import { DeleteEntityNetwork } from '@dcl/ecs/dist/serialization/crdt/network/deleteEntityNetwork'
26
- import { TransformSchema, COMPONENT_ID as TransformComponentId } from '@dcl/ecs/dist/components/manual/Transform'
27
-
28
- export type NetworkMessage = (
29
- | PutNetworkComponentMessage
30
- | DeleteComponentNetworkMessage
31
- | DeleteEntityNetworkMessage
32
- ) & { messageBuffer: Uint8Array }
33
-
34
- export type RegularMessage = (
35
- | PutComponentMessage
36
- | AuthoritativePutComponentMessage
37
- | DeleteComponentMessage
38
- | DeleteEntityMessage
39
- ) & {
40
- messageBuffer: Uint8Array
41
- }
42
- export function readMessages(data: Uint8Array): (NetworkMessage | RegularMessage)[] {
43
- const buffer = new ReadWriteByteBuffer(data)
44
- const messages: (NetworkMessage | RegularMessage)[] = []
45
- let header: CrdtMessageHeader | null
46
- while ((header = CrdtMessageProtocol.getHeader(buffer))) {
47
- const offset = buffer.currentReadOffset()
48
- let message: CrdtMessage | undefined = undefined
49
-
50
- // Network messages
51
- if (header.type === CrdtMessageType.DELETE_COMPONENT_NETWORK) {
52
- message = DeleteComponentNetwork.read(buffer)!
53
- } else if (header.type === CrdtMessageType.PUT_COMPONENT_NETWORK) {
54
- message = PutNetworkComponentOperation.read(buffer)!
55
- } else if (header.type === CrdtMessageType.DELETE_ENTITY_NETWORK) {
56
- message = DeleteEntityNetwork.read(buffer)!
57
- }
58
- // Regular messages
59
- else if (header.type === CrdtMessageType.PUT_COMPONENT) {
60
- message = PutComponentOperation.read(buffer)!
61
- } else if (header.type === CrdtMessageType.AUTHORITATIVE_PUT_COMPONENT) {
62
- message = AuthoritativePutComponentOperation.read(buffer)!
63
- } else if (header.type === CrdtMessageType.DELETE_COMPONENT) {
64
- message = DeleteComponent.read(buffer)!
65
- } else if (header.type === CrdtMessageType.DELETE_ENTITY) {
66
- message = DeleteEntity.read(buffer)!
67
- } else {
68
- // consume unknown messages
69
- buffer.incrementReadOffset(header.length)
70
- }
71
-
72
- if (message) {
73
- messages.push({
74
- ...message,
75
- messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
76
- })
77
- }
78
- }
79
- return messages
80
- }
81
-
82
- export function isNetworkMessage(message: ReceiveMessage): message is ReceiveNetworkMessage {
83
- return [
84
- CrdtMessageType.DELETE_COMPONENT_NETWORK,
85
- CrdtMessageType.DELETE_ENTITY_NETWORK,
86
- CrdtMessageType.PUT_COMPONENT_NETWORK
87
- ].includes(message.type)
88
- }
89
-
90
- export function networkMessageToLocal(
91
- message: ReceiveNetworkMessage,
92
- localEntityId: Entity,
93
- destinationBuffer: ByteBuffer,
94
- // Optional network parent component for transform fixing
95
- networkParentComponent?: typeof NetworkParent,
96
- // Force corrections - converts PUT_COMPONENT_NETWORK to authoritative_PUT_COMPONENT
97
- forceCorrections = false
98
- ): CrdtMessageBody {
99
- if (message.type === CrdtMessageType.PUT_COMPONENT_NETWORK) {
100
- let messageData = message.data
101
-
102
- // Fix transform parent if needed for Unity/engine processing
103
- if (message.componentId === TransformComponentId && networkParentComponent) {
104
- const parentNetwork = networkParentComponent.getOrNull(localEntityId)
105
- messageData = fixTransformParent(message, parentNetwork?.entityId)
106
- }
107
- if (forceCorrections) {
108
- // Use AUTHORITATIVE_PUT_COMPONENT for forced state updates
109
- AuthoritativePutComponentOperation.write(
110
- localEntityId,
111
- message.timestamp,
112
- message.componentId,
113
- messageData,
114
- destinationBuffer
115
- )
116
- return {
117
- type: CrdtMessageType.AUTHORITATIVE_PUT_COMPONENT,
118
- componentId: message.componentId,
119
- timestamp: message.timestamp,
120
- data: messageData,
121
- entityId: localEntityId
122
- }
123
- } else {
124
- // Normal PUT_COMPONENT conversion
125
- PutComponentOperation.write(localEntityId, message.timestamp, message.componentId, messageData, destinationBuffer)
126
- return {
127
- type: CrdtMessageType.PUT_COMPONENT,
128
- componentId: message.componentId,
129
- timestamp: message.timestamp,
130
- data: messageData,
131
- entityId: localEntityId
132
- }
133
- }
134
- } else if (message.type === CrdtMessageType.DELETE_COMPONENT_NETWORK) {
135
- DeleteComponent.write(localEntityId, message.componentId, message.timestamp, destinationBuffer)
136
- return {
137
- type: CrdtMessageType.DELETE_COMPONENT,
138
- componentId: message.componentId,
139
- timestamp: message.timestamp,
140
- entityId: localEntityId
141
- }
142
- } else if (message.type === CrdtMessageType.DELETE_ENTITY_NETWORK) {
143
- DeleteEntity.write(localEntityId, destinationBuffer)
144
- return {
145
- type: CrdtMessageType.DELETE_ENTITY,
146
- entityId: localEntityId
147
- }
148
- }
149
- throw 1
150
- }
151
-
152
- export function localMessageToNetwork(
153
- message: ReceiveMessage,
154
- network: INetowrkEntityType,
155
- destinationBuffer: ByteBuffer
156
- ) {
157
- if (message.type === CrdtMessageType.PUT_COMPONENT) {
158
- PutNetworkComponentOperation.write(
159
- network.entityId,
160
- message.timestamp,
161
- message.componentId,
162
- network.networkId,
163
- message.data,
164
- destinationBuffer
165
- )
166
- } else if (message.type === CrdtMessageType.DELETE_COMPONENT) {
167
- DeleteComponentNetwork.write(
168
- network.entityId,
169
- message.componentId,
170
- message.timestamp,
171
- network.networkId,
172
- destinationBuffer
173
- )
174
- } else if (message.type === CrdtMessageType.DELETE_ENTITY) {
175
- DeleteEntityNetwork.write(network.entityId, network.networkId, destinationBuffer)
176
- }
177
- }
178
-
179
- export function fixTransformParent(message: ReceiveMessage, parent?: Entity): Uint8Array {
180
- const buffer = new ReadWriteByteBuffer()
181
- const transform = 'data' in message && TransformSchema.deserialize(new ReadWriteByteBuffer(message.data))
182
-
183
- if (!transform) throw new Error('Invalid parent transform')
184
-
185
- // Generate new transform raw data with the parent
186
- const newTransform = { ...transform, parent }
187
- TransformSchema.serialize(newTransform, buffer)
188
- return buffer.toBinary()
189
- }
@@ -1,36 +0,0 @@
1
- import { getStorageServerUrl } from './storage-url'
2
- import { assertIsServer, wrapSignedFetch } from './utils'
3
-
4
- const MODULE_NAME = 'EnvVar'
5
-
6
- /**
7
- * EnvVar provides methods to fetch environment variables from the
8
- * Server Side Storage service. This module only works when running
9
- * on server-side scenes.
10
- */
11
- export const EnvVar = {
12
- /**
13
- * Fetches a specific environment variable by key as plain text.
14
- *
15
- * @param key - The name of the environment variable to fetch
16
- * @returns A promise that resolves to the plain text value, or empty string if not found
17
- * @throws Error if not running on a server-side scene
18
- */
19
- async get(key: string): Promise<string> {
20
- assertIsServer(MODULE_NAME)
21
-
22
- const baseUrl = await getStorageServerUrl()
23
- const url = `${baseUrl}/env/${encodeURIComponent(key)}`
24
-
25
- const [error, data] = await wrapSignedFetch<{ value: string }>({
26
- url
27
- })
28
-
29
- if (error) {
30
- console.error(`Failed to fetch environment variable '${key}': ${error}`)
31
- return ''
32
- }
33
-
34
- return data?.value ?? ''
35
- }
36
- }
@@ -1,12 +0,0 @@
1
- export { EnvVar } from './env-var'
2
- export {
3
- Storage,
4
- IStorage,
5
- ISceneStorage,
6
- IPlayerStorage,
7
- GetOptions,
8
- GetValuesOptions,
9
- GetValuesResult,
10
- SetOptions,
11
- StorageOptions
12
- } from './storage'
@@ -1,112 +0,0 @@
1
- export const MODULE_NAME = 'Storage'
2
-
3
- /**
4
- * Options for getValues pagination and filtering.
5
- */
6
- export interface GetValuesOptions {
7
- prefix?: string
8
- limit?: number
9
- offset?: number
10
- }
11
-
12
- /**
13
- * Result of getValues with pagination metadata.
14
- */
15
- export interface GetValuesResult {
16
- /** Key-value entries for the current page. */
17
- data: Array<{ key: string; value: unknown }>
18
- pagination: {
19
- offset: number
20
- total: number
21
- }
22
- }
23
-
24
- /**
25
- * Per-call options for Storage get().
26
- */
27
- export interface GetOptions {
28
- /**
29
- * When true, bypasses the read cache and forces a network read (the result
30
- * still refreshes the cache). Concurrent gets for the same key still share
31
- * one in-flight request. Default: false.
32
- */
33
- fresh?: boolean
34
- }
35
-
36
- /**
37
- * Per-call options for Storage set().
38
- */
39
- export interface SetOptions {
40
- /**
41
- * When true, skips the network write if the serialized value matches the
42
- * last value known to be stored for this key (from a previous successful
43
- * set(), get() or getValues()). Overrides the configured default. Default: true.
44
- */
45
- skipIfUnchanged?: boolean
46
- }
47
-
48
- /**
49
- * Module-wide configuration for Storage, applied to both scene-scoped and
50
- * player-scoped storage via Storage.configure().
51
- */
52
- export interface StorageOptions {
53
- /**
54
- * Default for set()'s skipIfUnchanged when not passed per call. A skip only
55
- * suppresses a proven no-op: the exact serialized value was confirmed stored
56
- * by a previous network round-trip within cacheMaxAgeMs. Default: true.
57
- */
58
- skipIfUnchanged?: boolean
59
- /**
60
- * When true, get() serves values from the local cache within cacheMaxAgeMs,
61
- * including confirmed "not found" results, without a network request.
62
- * Out-of-band writers (e.g. CLI storage commands) may not be visible for up
63
- * to cacheMaxAgeMs; pass { fresh: true } per call to force a network read.
64
- * Default: true.
65
- */
66
- cacheReads?: boolean
67
- /**
68
- * Max cache entries per scope (scene / player) before oldest entries are
69
- * evicted. Entries hold the serialized value body, so memory scales with
70
- * value size — this bounds entry count, not bytes. Infinity removes the bound.
71
- * Default: 512.
72
- */
73
- cacheMaxEntries?: number
74
- /**
75
- * Max age of a cache entry in milliseconds, bounding both read-cache
76
- * staleness and write-dedup trust; older entries are treated as unknown.
77
- * The default is deliberately short so out-of-band writers (CLI storage
78
- * commands, dashboards) become visible within a minute, while still
79
- * absorbing hot per-frame reads. Infinity never expires. Default: 60000 (1 minute).
80
- */
81
- cacheMaxAgeMs?: number
82
- }
83
-
84
- /**
85
- * Fully-resolved storage configuration shared by scene and player storage.
86
- * @internal
87
- */
88
- export interface StorageConfigState {
89
- skipIfUnchanged: boolean
90
- cacheReads: boolean
91
- cacheMaxEntries: number
92
- cacheMaxAgeMs: number
93
- }
94
-
95
- /**
96
- * Default values for the resolved storage configuration.
97
- * @internal
98
- */
99
- export const DEFAULT_STORAGE_CONFIG: Readonly<StorageConfigState> = {
100
- skipIfUnchanged: true,
101
- cacheReads: true,
102
- cacheMaxEntries: 512,
103
- cacheMaxAgeMs: 60 * 1000
104
- }
105
-
106
- /**
107
- * Creates a mutable config object, later mutated by Storage.configure().
108
- * @internal
109
- */
110
- export function createStorageConfig(overrides?: StorageOptions): StorageConfigState {
111
- return { ...DEFAULT_STORAGE_CONFIG, ...overrides }
112
- }