@rbxts/replecs 0.1.0-alpha9-test5 → 0.2.0-cdtest.1

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.
@@ -3,10 +3,13 @@
3
3
 
4
4
  local jecs = require "../jecs"
5
5
  local common = require "./common"
6
+ local customid = require "./customid"
6
7
  local utils = require "./utils"
7
8
 
8
9
  local cursor = utils.cursor
10
+
9
11
  type Cursor = utils.Cursor
12
+ type CustomId = customid.CustomId
10
13
 
11
14
  type Entity<T = any> = jecs.Entity<T>
12
15
  type Id<T = any> = jecs.Id<T>
@@ -14,24 +17,29 @@ type World = jecs.World
14
17
 
15
18
  type Array<T> = { T }
16
19
  type Map<K, V> = { [K]: V }
20
+ type Set<T> = { [T]: boolean }
17
21
  type Disconnect = () -> ()
18
22
 
23
+ type CommandBuffer = {
24
+ tags: { [Entity]: boolean },
25
+ components: { [Entity]: { value: any } },
26
+ unreliable_components: { [Entity]: { value: any } },
27
+ remove: { [Entity]: boolean },
28
+ pairs: { [Entity]: Set<Entity> },
29
+ pairs_values: { [Entity]: { [Entity]: { value: any } } },
30
+ pairs_remove: { [Entity]: Set<Entity> },
31
+ pairs_clear: Set<Entity>,
32
+ }
33
+
19
34
  type HookMethod =
20
35
  & ((
21
36
  self: Client,
22
- entity: Entity,
23
- action: "change",
24
- relation: Entity,
37
+ action: "changed",
38
+ relation: Id,
25
39
  callback: (entity: Entity, id: Id, value: any) -> ()
26
40
  ) -> Disconnect)
27
- & ((
28
- self: Client,
29
- entity: Entity,
30
- action: "removed",
31
- relation: Entity,
32
- callback: (entity: Entity, id: Id) -> ()
33
- ) -> Disconnect)
34
- & (self: Client, entity: Entity, action: "deleted", callback: (entity: Entity) -> ()) -> Disconnect
41
+ & ((self: Client, action: "removed", relation: Id, callback: (entity: Entity, id: Id) -> ()) -> Disconnect)
42
+ & (self: Client, action: "deleted", entity: Entity, callback: (entity: Entity) -> ()) -> Disconnect
35
43
 
36
44
  type ChangedHooksEntry = {
37
45
  overrides: boolean,
@@ -46,6 +54,8 @@ type DeletedHookEntry = {
46
54
  callbacks: Array<(entity: Entity) -> ()>,
47
55
  }
48
56
 
57
+ type CustomIdMap = { [Entity]: CustomId | Entity }
58
+
49
59
  export type Client = {
50
60
  world: World,
51
61
  inited: boolean?,
@@ -53,7 +63,7 @@ export type Client = {
53
63
  is_replicating: boolean,
54
64
  after_replication_callbacks: { () -> () },
55
65
 
56
- is_dirty: boolean,
66
+ is_shared_dirty: boolean,
57
67
  requires_shared_lookup: { Entity },
58
68
  components: common.Components,
59
69
  shared: common.Shared,
@@ -61,7 +71,9 @@ export type Client = {
61
71
  server_ids: { [number]: Entity },
62
72
  client_ids: { [Entity]: number },
63
73
  ordered_creation: boolean,
64
- custom_ids: { [Entity]: (any) -> Entity },
74
+ registered_custom_ids: { [CustomId]: boolean },
75
+ command_buffers: { [Entity]: CommandBuffer }?,
76
+ command_buffers_active: boolean,
65
77
 
66
78
  init: (self: Client, world: World?) -> (),
67
79
  destroy: (self: Client) -> (),
@@ -69,6 +81,9 @@ export type Client = {
69
81
  get_server_entity: (self: Client, client_entity: Entity) -> number?,
70
82
  get_client_entity: (self: Client, server_entity: number) -> Entity?,
71
83
 
84
+ register_entity: (self: Client, entity: Entity, server_entity: number) -> (),
85
+ unregister_entity: (self: Client, entity: Entity) -> (),
86
+
72
87
  hooks: common.WorldHooks,
73
88
  hooked: Array<() -> ()>,
74
89
 
@@ -77,27 +92,34 @@ export type Client = {
77
92
  hook: HookMethod,
78
93
  override: HookMethod,
79
94
  addition_hooks: Array<(entity: Entity) -> ()>,
80
- entity_hooks: {
81
- [Entity]: {
82
- changed: Map<Entity, ChangedHooksEntry>,
83
- removed: Map<Entity, RemovedHooksEntry>,
84
- deleted: DeletedHookEntry?,
85
- },
95
+ network_hooks: {
96
+ deleted: { [Entity]: DeletedHookEntry },
97
+ changed: { [Id]: ChangedHooksEntry },
98
+ removed: { [Id]: RemovedHooksEntry },
86
99
  },
87
100
 
88
101
  encode_component: (self: Client, component: Entity) -> number,
89
102
  decode_component: (self: Client, component: number) -> Entity,
103
+ register_custom_id: (self: Client, custom_id: CustomId) -> (),
90
104
 
91
105
  apply_updates: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
92
106
  apply_unreliable: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
93
107
  apply_full: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
108
+
109
+ generate_handshake: (self: Client) -> common.HandshakeInfo,
110
+ verify_handshake: (self: Client, handshake: common.HandshakeInfo) -> (boolean, string?),
94
111
  }
95
112
 
113
+ local client_replicator = {}
114
+ client_replicator.__index = client_replicator
115
+
96
116
  local GLOBAL_ID_OFFSET = 10
117
+
97
118
  local ENTITY_ID_TYPES = {
98
119
  entity = 1,
99
- custom = 2,
100
- shared = 3,
120
+ custom_handler = 2,
121
+ custom_id = 3,
122
+ shared = 4,
101
123
  }
102
124
  local PACKET_TYPES = {
103
125
  full = 1,
@@ -105,7 +127,6 @@ local PACKET_TYPES = {
105
127
  reliable = 3,
106
128
  }
107
129
 
108
- local ECS_NAME = jecs.Name
109
130
  local WILDCARD = jecs.Wildcard
110
131
 
111
132
  local function PAIR(first: Entity, second: Entity): Entity
@@ -131,20 +152,97 @@ local function get_or_create_entity(client: Client, server_id: number): Entity
131
152
  end
132
153
  end
133
154
 
134
- local function network_entity_set(client: Client, entity: Entity, id: Id, value: any, changed_hooks: ChangedHooksEntry?)
135
- if changed_hooks then
136
- if not changed_hooks.overrides then
155
+ local function get_command_buffer(client: Client, entity: Entity): CommandBuffer
156
+ local command_buffers = client.command_buffers
157
+
158
+ if not command_buffers then
159
+ error "attempted to use a command buffer without having one. (this should never happen)"
160
+ end
161
+
162
+ local command_buffer = command_buffers[entity :: any]
163
+ if not command_buffer then
164
+ command_buffer = {
165
+ tags = {},
166
+ components = {},
167
+ unreliable_components = {},
168
+ remove = {},
169
+ pairs = {},
170
+ pairs_values = {},
171
+ pairs_remove = {},
172
+ pairs_clear = {},
173
+ }
174
+ command_buffers[entity :: any] = command_buffer
175
+ end
176
+ return command_buffer
177
+ end
178
+
179
+ function client_replicator.entity_set(
180
+ client: Client,
181
+ entity: Entity,
182
+ id: Id,
183
+ value: any,
184
+ changed_hooks: ChangedHooksEntry?
185
+ )
186
+ if client.command_buffers_active then
187
+ local command_buffer = get_command_buffer(client, entity)
188
+ command_buffer.components[id] = { value = value }
189
+ else
190
+ if changed_hooks then
191
+ if not changed_hooks.overrides then
192
+ client.world:set(entity, id, value)
193
+ end
194
+ for _, callback in changed_hooks.callbacks do
195
+ callback(entity, id, value)
196
+ end
197
+ else
137
198
  client.world:set(entity, id, value)
138
199
  end
139
- for _, callback in changed_hooks.callbacks do
140
- callback(entity, id, value)
200
+ end
201
+ end
202
+
203
+ function client_replicator.entity_set_unreliable(
204
+ client: Client,
205
+ entity: Entity,
206
+ component: Entity,
207
+ value: any,
208
+ changed_hooks: ChangedHooksEntry?
209
+ )
210
+ if client.command_buffers_active then
211
+ local command_buffer = get_command_buffer(client, entity)
212
+ command_buffer.unreliable_components[component] = value
213
+ else
214
+ client_replicator.entity_set(client, entity, component, value, changed_hooks)
215
+ end
216
+ end
217
+
218
+ function client_replicator.entity_set_pair(
219
+ client: Client,
220
+ entity: Entity,
221
+ relation: Entity,
222
+ target: Entity,
223
+ value: any,
224
+ changed_hooks: ChangedHooksEntry?
225
+ )
226
+ if client.command_buffers_active then
227
+ local command_buffer = get_command_buffer(client, entity)
228
+ local pair_relation = command_buffer.pairs_values[relation]
229
+
230
+ if pair_relation then
231
+ pair_relation[target] = { value = value }
232
+ else
233
+ command_buffer.pairs_values[relation] = { [target] = value }
141
234
  end
142
235
  else
143
- client.world:set(entity, id, value)
236
+ client_replicator.entity_set(client, entity, PAIR(relation, target), value, changed_hooks)
144
237
  end
145
238
  end
146
239
 
147
- local function network_entity_add(client: Client, entity: Entity, component: Entity, changed_hooks: ChangedHooksEntry?)
240
+ function client_replicator.entity_add(
241
+ client: Client,
242
+ entity: Entity,
243
+ component: Entity,
244
+ changed_hooks: ChangedHooksEntry?
245
+ )
148
246
  if changed_hooks then
149
247
  if not changed_hooks.overrides then
150
248
  client.world:add(entity, component)
@@ -157,77 +255,129 @@ local function network_entity_add(client: Client, entity: Entity, component: Ent
157
255
  end
158
256
  end
159
257
 
160
- local function network_entity_remove(client: Client, entity: Entity, id: Entity, removed_hooks: RemovedHooksEntry?)
161
- if removed_hooks then
162
- for _, callback in removed_hooks.callbacks do
163
- callback(entity, id)
258
+ function client_replicator.entity_add_pair(
259
+ client: Client,
260
+ entity: Entity,
261
+ relation: Entity,
262
+ target: Entity,
263
+ changed_hooks: ChangedHooksEntry?
264
+ )
265
+ if client.command_buffers_active then
266
+ local command_buffer = get_command_buffer(client, entity)
267
+ local pair_relation = command_buffer.pairs[relation]
268
+
269
+ if pair_relation then
270
+ pair_relation[target] = true
271
+ else
272
+ command_buffer.pairs[relation] = { [target] = true }
164
273
  end
165
- if not removed_hooks.overrides then
274
+ else
275
+ client_replicator.entity_add(client, entity, PAIR(relation, target), changed_hooks)
276
+ end
277
+ end
278
+
279
+ function client_replicator.entity_remove(client: Client, entity: Entity, id: Entity, removed_hooks: RemovedHooksEntry?)
280
+ if client.command_buffers_active then
281
+ local command_buffer = get_command_buffer(client, entity)
282
+ command_buffer.remove[id] = true
283
+ else
284
+ if removed_hooks then
285
+ for _, callback in removed_hooks.callbacks do
286
+ callback(entity, id)
287
+ end
288
+ if not removed_hooks.overrides then
289
+ client.world:remove(entity, id)
290
+ end
291
+ else
166
292
  client.world:remove(entity, id)
167
293
  end
168
- else
169
- client.world:remove(entity, id)
170
294
  end
171
295
  end
172
296
 
173
- local function network_entity_remove_relations(
297
+ function client_replicator.entity_remove_pair(
174
298
  client: Client,
175
299
  entity: Entity,
176
300
  relation: Entity,
301
+ target: Entity,
177
302
  removed_hooks: RemovedHooksEntry?
178
303
  )
179
- local world = client.world
180
- local entity_index = jecs.entity_index_try_get_fast(world.entity_index :: any, entity :: any)
181
- if not entity_index then
182
- return
304
+ if client.command_buffers_active then
305
+ local command_buffer = get_command_buffer(client, entity)
306
+ local pair_relation = command_buffer.pairs_remove[relation]
307
+
308
+ if pair_relation then
309
+ pair_relation[target] = true
310
+ else
311
+ command_buffer.pairs_remove[relation] = { [target] = true }
312
+ end
313
+ else
314
+ client_replicator.entity_remove(client, entity, PAIR(relation, target), removed_hooks)
183
315
  end
184
- local archetype = entity_index.archetype
316
+ end
185
317
 
186
- local wildcard = PAIR(relation, WILDCARD) :: any
187
- local idr = world.component_index[wildcard]
188
- if not idr then
189
- return
190
- end
318
+ function client_replicator.entity_remove_relations(
319
+ client: Client,
320
+ entity: Entity,
321
+ relation: Entity,
322
+ removed_hooks: RemovedHooksEntry?
323
+ )
324
+ if client.command_buffers_active then
325
+ local command_buffer = get_command_buffer(client, entity)
326
+ command_buffer.pairs_clear[relation] = true
327
+ else
328
+ local world = client.world
329
+ local entity_index = jecs.entity_index_try_get_fast(world.entity_index :: any, entity :: any)
330
+ if not entity_index then
331
+ return
332
+ end
333
+ local archetype = entity_index.archetype
191
334
 
192
- local archetype_id = archetype.id
193
- local count = idr.counts[archetype_id]
194
- if not count then
195
- return
196
- end
335
+ local wildcard = PAIR(relation, WILDCARD) :: any
336
+ local idr = world.component_index[wildcard]
337
+ if not idr then
338
+ return
339
+ end
197
340
 
198
- local start = idr.records[archetype_id]
199
- if not start then
200
- return
201
- end
341
+ local archetype_id = archetype.id
342
+ local count = idr.counts[archetype_id]
343
+ if not count then
344
+ return
345
+ end
202
346
 
203
- if removed_hooks then
204
- for i = start, start + count - 1 do
205
- local pair = archetype.types[i]
206
- for _, callback in removed_hooks.callbacks do
207
- callback(entity, pair)
347
+ local start = idr.records[archetype_id]
348
+ if not start then
349
+ return
350
+ end
351
+
352
+ if removed_hooks then
353
+ for i = start, start + count - 1 do
354
+ local pair = archetype.types[i]
355
+ for _, callback in removed_hooks.callbacks do
356
+ callback(entity, pair)
357
+ end
358
+ if not removed_hooks.overrides then
359
+ world:remove(entity, pair :: any)
360
+ end
208
361
  end
209
- if not removed_hooks.overrides then
362
+ else
363
+ for i = start, start + count - 1 do
364
+ local pair = archetype.types[i]
210
365
  world:remove(entity, pair :: any)
211
366
  end
212
367
  end
213
- else
214
- for i = start, start + count - 1 do
215
- local pair = archetype.types[i]
216
- world:remove(entity, pair :: any)
217
- end
218
368
  end
219
369
  end
220
370
 
221
- local function encode_component(client: Client, component: Entity): number
222
- local encoded = client.shared.ids[component]
371
+ function client_replicator.encode_component(client: Client, component: Entity): number
372
+ local encoded = client.shared.components.members[component]
223
373
  if not encoded then
224
374
  utils.logerror(`attempted to encode a non-shared component `, utils.logcomponent(client.world, component))
225
375
  return 0
226
376
  end
227
377
  return encoded
228
378
  end
229
- local function decode_component(client: Client, encoded: number): Entity
230
- local component = client.shared.components[encoded]
379
+ function client_replicator.decode_component(client: Client, encoded: number): Entity
380
+ local component = client.shared.components.indexes[encoded]
231
381
  if not component then
232
382
  print("NON SHARED COMPONENT", encoded, client.shared.components)
233
383
  error "attemped to decode a non shared component"
@@ -235,9 +385,10 @@ local function decode_component(client: Client, encoded: number): Entity
235
385
  return component
236
386
  end
237
387
 
238
- local function read_component_id(client: Client, c: Cursor): Entity
239
- local shared_id = cursor.readu8(c)
240
- local component = client.shared.components[shared_id]
388
+ function client_replicator.read_component_id(client: Client, c: Cursor): Entity
389
+ local shared_components = client.shared.components
390
+ local shared_id = cursor.read_span(c, #shared_components.keys)
391
+ local component = shared_components.indexes[shared_id]
241
392
  if not component then
242
393
  print("NON SHARED COMPONENT", shared_id, client.shared.components)
243
394
  error "received a non shared component"
@@ -245,11 +396,11 @@ local function read_component_id(client: Client, c: Cursor): Entity
245
396
  return component
246
397
  end
247
398
 
248
- local function read_component_value(client: Client, component: Entity, c: Cursor, variants: { any }?): any
399
+ function client_replicator.read_component_value(client: Client, component: Entity, c: Cursor, variants: { any }?): any
249
400
  local serdes = client.shared.serdes[component]
250
401
 
251
402
  if serdes then
252
- local bytespan = client.shared.bytespan[component] or cursor.read_vlq(c)
403
+ local bytespan = serdes.bytespan or cursor.read_vlq(c)
253
404
 
254
405
  local appended = cursor.read_buffer(c, bytespan)
255
406
  local serdes_variants: { any }? = nil
@@ -275,13 +426,13 @@ local function read_component_value(client: Client, component: Entity, c: Cursor
275
426
  end
276
427
  end
277
428
 
278
- local function read_component(client: Client, c: Cursor, variants: { any }?): (Entity, any)
279
- local component = read_component_id(client, c)
280
- local value = read_component_value(client, component, c, variants)
429
+ function client_replicator.read_component(client: Client, c: Cursor, variants: { any }?): (Entity, any)
430
+ local component = client_replicator.read_component_id(client, c)
431
+ local value = client_replicator.read_component_value(client, component, c, variants)
281
432
  return component, value
282
433
  end
283
434
 
284
- local function resolve_global(client: Client, global_id: number): Entity
435
+ function client_replicator.resolve_global(client: Client, global_id: number): Entity
285
436
  if not client.global_handler then
286
437
  error "global id parser not set, consider using client:handle_global()"
287
438
  end
@@ -289,112 +440,145 @@ local function resolve_global(client: Client, global_id: number): Entity
289
440
  return parsed
290
441
  end
291
442
 
292
- local function read_entity_id(client: Client, c: Cursor, variants: { any }?): (Entity?, number?)
443
+ -- returns entity, server_id, id_type
444
+ function client_replicator.read_entity_id(client: Client, c: Cursor): (Entity?, number?, number?)
293
445
  local id_type = cursor.readu8(c)
294
446
 
295
447
  if id_type <= GLOBAL_ID_OFFSET then
296
- if id_type == ENTITY_ID_TYPES.entity then
448
+ if
449
+ id_type == ENTITY_ID_TYPES.entity
450
+ or id_type == ENTITY_ID_TYPES.custom_id
451
+ or id_type == ENTITY_ID_TYPES.custom_handler
452
+ then
297
453
  local server_id = cursor.readu40(c)
298
- return client.server_ids[server_id], server_id
299
- elseif id_type == ENTITY_ID_TYPES.custom then
300
- local component, value = read_component(client, c, variants)
301
- local custom_getter = client.custom_ids[component]
302
- if not custom_getter then
303
- error(
304
- `received a custom id for a non custom component, consider adding custom_id to component: {client.world:get(
305
- component,
306
- ECS_NAME
307
- ) or "(no name)"} id: {component}`
308
- )
309
- end
310
- return custom_getter(value), nil
454
+ return client.server_ids[server_id], server_id, id_type
311
455
  elseif id_type == ENTITY_ID_TYPES.shared then
312
- return read_component_id(client, c), nil
456
+ return client_replicator.read_component_id(client, c), nil, id_type
313
457
  end
314
- error(`malformed entity id {id_type} ` .. cursor.readu32(c))
458
+ error(`malformed entity type {id_type} ` .. cursor.readu32(c))
315
459
  else
316
- return resolve_global(client, id_type - GLOBAL_ID_OFFSET), nil
460
+ return client_replicator.resolve_global(client, id_type - GLOBAL_ID_OFFSET), nil, id_type
317
461
  end
318
462
  end
319
463
 
320
- local function process_entity_id(client: Client, c: Cursor, variants: { any }?)
321
- local entity, server_id = read_entity_id(client, c, variants)
464
+ function client_replicator.process_entity_id(
465
+ client: Client,
466
+ c: Cursor,
467
+ custom_ids: CustomIdMap,
468
+ process: (entity: Entity) -> ()
469
+ )
470
+ local entity, server_id, id_type = client_replicator.read_entity_id(client, c)
471
+
322
472
  if entity then
323
- return entity
473
+ -- skip customid, keep cursor offset
474
+ if id_type == ENTITY_ID_TYPES.custom_id then
475
+ client_replicator.read_component_id(client, c)
476
+ elseif id_type == ENTITY_ID_TYPES.custom_handler then
477
+ cursor.readu8(c)
478
+ end
479
+
480
+ return process(entity)
324
481
  else
325
- return get_or_create_entity(client, server_id :: number)
482
+ if id_type == ENTITY_ID_TYPES.custom_id or id_type == ENTITY_ID_TYPES.custom_handler then
483
+ if id_type == ENTITY_ID_TYPES.custom_id then
484
+ local component_id = client_replicator.read_component_id(client, c)
485
+ custom_ids[server_id :: any] = component_id
486
+ elseif id_type == ENTITY_ID_TYPES.custom_handler then
487
+ local shared_custom_id = cursor.readu8(c)
488
+ local handler = client.shared.custom_ids.indexes[shared_custom_id]
489
+
490
+ if not handler then
491
+ utils.logerror "attempted to use a custom id that wasn't registered"
492
+ end
493
+ custom_ids[server_id :: any] = handler
494
+ end
495
+
496
+ if client.command_buffers_active then
497
+ -- negative id means it's a custom id,
498
+ -- it needs to be translated first
499
+ process(-server_id :: any)
500
+ else
501
+ client.command_buffers_active = true
502
+ process(-server_id :: any)
503
+ client.command_buffers_active = false
504
+ end
505
+ else
506
+ local new_entity = get_or_create_entity(client, server_id :: number)
507
+ process(new_entity)
508
+ end
326
509
  end
327
510
  end
328
511
 
329
- local function process_entity_relations(
512
+ function client_replicator.process_entity_relations(
330
513
  client: Client,
331
514
  entity: jecs.Entity,
332
- changed_hooks: Map<Entity, ChangedHooksEntry>?,
333
515
  c: Cursor,
334
- variants: { any }?
516
+ custom_ids: CustomIdMap
335
517
  )
336
518
  local components = client.components
337
- local relation = read_component_id(client, c)
519
+ local relation = client_replicator.read_component_id(client, c)
338
520
  local total_targets = cursor.read_vlq(c)
339
- local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
521
+ local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
340
522
 
341
523
  for _ = 1, total_targets do
342
- local target = process_entity_id(client, c, variants)
343
- network_entity_add(client, entity, PAIR(relation, target), pair_hooks)
524
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
525
+ client_replicator.entity_add_pair(client, entity, relation, target, pair_hooks)
526
+ end)
344
527
  end
345
528
  end
346
529
 
347
- local function process_entity_relation_values(
530
+ function client_replicator.process_entity_relation_values(
348
531
  client: Client,
349
532
  entity: jecs.Entity,
350
- changed_hooks: Map<Entity, ChangedHooksEntry>?,
351
533
  c: Cursor,
534
+ custom_ids: CustomIdMap,
352
535
  variants: { any }?
353
536
  )
354
- local relation = read_component_id(client, c)
537
+ local relation = client_replicator.read_component_id(client, c)
355
538
  local total_targets = cursor.read_vlq(c)
356
539
  local components = client.components
357
- local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
540
+ local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
358
541
 
359
542
  for _ = 1, total_targets do
360
- local value = read_component_value(client, relation, c, variants)
361
- local target = process_entity_id(client, c, variants)
362
- network_entity_set(client, entity, PAIR(relation, target), value, pair_hooks)
543
+ local value = client_replicator.read_component_value(client, relation, c, variants)
544
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
545
+ client_replicator.entity_set_pair(client, entity, relation, target, value, pair_hooks)
546
+ end)
363
547
  end
364
548
  end
365
549
 
366
- local function process_entity(client: Client, c: Cursor, variants: { any }?)
367
- local entity = process_entity_id(client, c, variants)
368
- local entity_hooks = client.entity_hooks[entity]
369
- local changed_hooks = entity_hooks and entity_hooks.changed
370
- local components = client.components
550
+ function client_replicator.process_entity(client: Client, c: Cursor, custom_ids: CustomIdMap, variants: { any }?)
551
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
552
+ local changed_hooks = client.network_hooks.changed
553
+ local components = client.components
371
554
 
372
- local total_tags = cursor.read_vlq(c)
373
- for _ = 1, total_tags do
374
- local tag = read_component_id(client, c)
375
- local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
376
- network_entity_add(client, entity, tag, tag_hooks)
377
- end
555
+ local total_tags = cursor.read_vlq(c)
556
+ for _ = 1, total_tags do
557
+ local tag = client_replicator.read_component_id(client, c)
558
+ local tag_hooks = changed_hooks[PAIR(components.reliable, tag)]
559
+ client_replicator.entity_add(client, entity, tag, tag_hooks)
560
+ end
378
561
 
379
- local total_components = cursor.read_vlq(c)
380
- for _ = 1, total_components do
381
- local component, value = read_component(client, c, variants)
382
- local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
383
- network_entity_set(client, entity, component, value, component_hooks)
384
- end
562
+ local total_components = cursor.read_vlq(c)
563
+ for _ = 1, total_components do
564
+ local component, value = client_replicator.read_component(client, c, variants)
565
+ local component_hooks = changed_hooks[PAIR(components.reliable, component)]
566
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
567
+ end
385
568
 
386
- local total_pairs = cursor.read_vlq(c)
387
- for _ = 1, total_pairs do
388
- process_entity_relations(client, entity, changed_hooks, c, variants)
389
- end
569
+ local total_pairs = cursor.read_vlq(c)
570
+ for _ = 1, total_pairs do
571
+ client_replicator.process_entity_relations(client, entity, c, custom_ids)
572
+ end
390
573
 
391
- local total_pairs_values = cursor.read_vlq(c)
392
- for _ = 1, total_pairs_values do
393
- process_entity_relation_values(client, entity, changed_hooks, c, variants)
394
- end
574
+ local total_pairs_values = cursor.read_vlq(c)
575
+ for _ = 1, total_pairs_values do
576
+ client_replicator.process_entity_relation_values(client, entity, c, custom_ids, variants)
577
+ end
578
+ end)
395
579
  end
396
580
 
397
- local function finish_replication(client: Client)
581
+ function client_replicator.finish_replication(client: Client)
398
582
  client.is_replicating = false
399
583
  for _, callback in client.after_replication_callbacks do
400
584
  callback()
@@ -402,10 +586,10 @@ local function finish_replication(client: Client)
402
586
  table.clear(client.after_replication_callbacks)
403
587
  end
404
588
 
405
- local function resolve_dirty(client: Client)
406
- if client.is_dirty then
407
- client.shared = utils.create_shared_lookup(client.world, client.components)
408
- client.is_dirty = false :: true -- wtf
589
+ function client_replicator.resolve_dirty(client: Client)
590
+ if client.is_shared_dirty then
591
+ client.shared = utils.resolved_shared(client.world, client.components, client.registered_custom_ids)
592
+ client.is_shared_dirty = false :: true -- wtf
409
593
  end
410
594
  end
411
595
 
@@ -433,15 +617,300 @@ local function read_vlq_bitmask(c: Cursor, mask: number, bit: number): number
433
617
  end
434
618
  end
435
619
 
436
- local function apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
620
+ function client_replicator.apply_command_buffer(
621
+ client: Client,
622
+ entity: Entity,
623
+ command_buffer: CommandBuffer,
624
+ request: (e: Entity, ...any) -> Entity?
625
+ )
626
+ local components = client.components
627
+
628
+ for tag in command_buffer.tags do
629
+ local tag_hooks = client.network_hooks.changed[PAIR(components.reliable, tag)]
630
+ client_replicator.entity_add(client, entity, tag, tag_hooks)
631
+ end
632
+ for component, value in command_buffer.components do
633
+ local component_hooks = client.network_hooks.changed[PAIR(components.reliable, component)]
634
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
635
+ end
636
+ for component, value in command_buffer.unreliable_components do
637
+ local component_hooks = client.network_hooks.changed[PAIR(components.unreliable, component)]
638
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
639
+ end
640
+ for component in command_buffer.remove do
641
+ local component_hooks = client.network_hooks.removed[PAIR(components.reliable, component)]
642
+ client_replicator.entity_remove(client, entity, component, component_hooks)
643
+ end
644
+ for relation, targets in command_buffer.pairs do
645
+ local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
646
+ for target in targets do
647
+ local client_target = request(target)
648
+ if client_target then
649
+ client_replicator.entity_add_pair(client, entity, relation, client_target, pair_hooks)
650
+ end
651
+ end
652
+ end
653
+ for relation, targets in command_buffer.pairs_values do
654
+ local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
655
+ for target, value in targets do
656
+ local client_target = request(target)
657
+ if client_target then
658
+ client_replicator.entity_set_pair(client, entity, relation, client_target, value.value, pair_hooks)
659
+ end
660
+ end
661
+ end
662
+ for relation, targets in command_buffer.pairs_remove do
663
+ local pair_hooks = client.network_hooks.removed[PAIR(components.pair, relation)]
664
+ for target in targets do
665
+ local client_target = request(target)
666
+ if client_target then
667
+ client_replicator.entity_remove_pair(client, entity, relation, client_target, pair_hooks)
668
+ end
669
+ end
670
+ end
671
+ for relation in command_buffer.pairs_clear do
672
+ local pair_hooks = client.network_hooks.removed[PAIR(components.pair, relation)]
673
+ client_replicator.entity_remove_relations(client, entity, relation, pair_hooks)
674
+ end
675
+ end
676
+
677
+ function client_replicator.find_component_in_buffer(command_buffer: CommandBuffer, component: Entity): { value: any }?
678
+ return command_buffer.components[component] or command_buffer.unreliable_components[component]
679
+ end
680
+
681
+ function client_replicator.find_has_in_buffer(command_buffer: CommandBuffer, tag: Entity): boolean
682
+ return (command_buffer.tags[tag] ~= nil)
683
+ or (command_buffer.components[tag] ~= nil)
684
+ or (command_buffer.unreliable_components[tag] ~= nil)
685
+ end
686
+ function client_replicator.find_pair_in_buffer(
687
+ command_buffer: CommandBuffer,
688
+ relation: Entity,
689
+ target: Entity,
690
+ find: (unprocessed_entity: Entity) -> (Entity?, Entity)
691
+ ): (boolean, any)
692
+ local value_relations = command_buffer.pairs_values[relation]
693
+
694
+ if value_relations then
695
+ for unprocessed_target, value in value_relations do
696
+ local found = find(unprocessed_target)
697
+ if found == target then
698
+ return true, value.value
699
+ end
700
+ end
701
+ end
702
+
703
+ local relations = command_buffer.pairs[relation]
704
+ if relations then
705
+ for unprocessed_target in relations do
706
+ local found = find(unprocessed_target)
707
+ if found == target then
708
+ return true, nil
709
+ end
710
+ end
711
+ end
712
+ return false, nil
713
+ end
714
+
715
+ function client_replicator.find_target_in_buffer(
716
+ command_buffer: CommandBuffer,
717
+ relation: Entity,
718
+ index: number?
719
+ ): Entity?
720
+ local i = 0
721
+
722
+ local pairs = command_buffer.pairs[relation]
723
+ if pairs then
724
+ for target in pairs do
725
+ if i == (index or 0) then
726
+ return target
727
+ end
728
+ i += 1
729
+ end
730
+ end
731
+ local pairs_values = command_buffer.pairs_values[relation]
732
+ if pairs_values then
733
+ for target in pairs_values do
734
+ if i == (index or 0) then
735
+ return target
736
+ end
737
+ i += 1
738
+ end
739
+ end
740
+ return nil
741
+ end
742
+
743
+ function client_replicator.finish_custom_ids(
744
+ client: Client,
745
+ custom_ids: CustomIdMap,
746
+ command_buffers: { [Entity]: CommandBuffer }
747
+ )
748
+ local funcs = {} -- this is so process can use create_context even if its defined after
749
+ local processed: { [Entity]: boolean } = {}
750
+
751
+ local function find(unprocessed_entity: Entity): (Entity?, Entity)
752
+ -- negative entities means they are still server ids that haven't been processed yet
753
+ if (unprocessed_entity :: any) > 0 then
754
+ return unprocessed_entity, unprocessed_entity
755
+ end
756
+ local server_entity = -(unprocessed_entity :: any)
757
+ local client_entity = client.server_ids[server_entity]
758
+ if client_entity then
759
+ return client_entity, server_entity
760
+ end
761
+ return nil, server_entity
762
+ end
763
+
764
+ local function process(unprocessed_entity: Entity, custom_handler: (CustomId | Entity)?): Entity?
765
+ local client_entity, server_entity = find(unprocessed_entity)
766
+ if client_entity then
767
+ return client_entity
768
+ end
769
+
770
+ if processed[server_entity] then
771
+ return nil
772
+ end
773
+
774
+ local new_entity: Entity? = nil
775
+ local command_buffer = command_buffers[unprocessed_entity :: any]
776
+ custom_handler = custom_handler or custom_ids[server_entity]
777
+
778
+ if type(custom_handler) == "number" then
779
+ local component: Entity = custom_handler :: any
780
+
781
+ local handler = client.world:get(component, client.components.custom_handler)
782
+ if not handler then
783
+ return utils.logerror(
784
+ `received a custom id for a non custom component, consider adding custom_handler to component: {utils.logcomponent(
785
+ client.world,
786
+ component
787
+ )}`
788
+ )
789
+ end
790
+ if command_buffer then
791
+ local value = client_replicator.find_component_in_buffer(command_buffer, component)
792
+ if value then
793
+ new_entity = handler(value.value)
794
+ else
795
+ utils.logwarn(
796
+ `No component found for custom id handler: {utils.logcomponent(client.world, component)}`
797
+ )
798
+ new_entity = handler(nil)
799
+ end
800
+ else
801
+ utils.logwarn(`No component found for custom id handler: {utils.logcomponent(client.world, component)}`)
802
+ new_entity = handler(nil)
803
+ end
804
+ else
805
+ local handle_callback = (custom_handler :: CustomId).handle_callback
806
+ if not handle_callback then
807
+ return utils.logerror(
808
+ "no handler callback was set for custom id:",
809
+ (custom_handler :: CustomId).identifier
810
+ )
811
+ end
812
+ new_entity = handle_callback(funcs.create_context(unprocessed_entity))
813
+ end
814
+
815
+ if new_entity then
816
+ client.server_ids[server_entity :: any] = new_entity
817
+ if command_buffer then
818
+ client_replicator.apply_command_buffer(client, new_entity, command_buffer, process)
819
+ command_buffers[unprocessed_entity] = nil
820
+ end
821
+ end
822
+
823
+ processed[server_entity] = true
824
+ return new_entity
825
+ end
826
+
827
+ local function create_context(unprocessed_entity: Entity)
828
+ local command_buffer = command_buffers[unprocessed_entity]
829
+
830
+ local function component(component: Entity): any
831
+ if not command_buffer then
832
+ return nil
833
+ end
834
+ local val = client_replicator.find_component_in_buffer(command_buffer, component)
835
+ return val and val.value
836
+ end
837
+ local function target(relation: Entity, index: number?): Entity
838
+ if not command_buffer then
839
+ return nil :: any
840
+ end
841
+ local server_target = client_replicator.find_target_in_buffer(command_buffer, relation, index)
842
+ if server_target then
843
+ return process(server_target :: any) :: Entity
844
+ end
845
+ return nil :: any
846
+ end
847
+ local function pair_value(relation: Entity, target: Entity): any
848
+ if not command_buffer then
849
+ return nil :: any
850
+ end
851
+ local _, value = client_replicator.find_pair_in_buffer(command_buffer, relation, target, find)
852
+ return value
853
+ end
854
+ local function has_pair(relation: Entity, target: Entity): boolean
855
+ if not command_buffer then
856
+ return nil :: any
857
+ end
858
+ local has = client_replicator.find_pair_in_buffer(command_buffer, relation, target, find)
859
+ return has
860
+ end
861
+ local function has(tag: Entity): boolean
862
+ if not command_buffer then
863
+ return nil :: any
864
+ end
865
+ return client_replicator.find_has_in_buffer(command_buffer, tag)
866
+ end
867
+ local function entity(server_entity: number): Entity
868
+ return process(-server_entity :: any) :: Entity
869
+ end
870
+
871
+ local context: customid.HandleContext = {
872
+ entity_id = math.abs(unprocessed_entity :: any),
873
+ has = has,
874
+ component = component,
875
+ target = target,
876
+ pair_value = pair_value,
877
+ has_pair = has_pair,
878
+ entity = entity,
879
+ }
880
+
881
+ return context
882
+ end
883
+ funcs.create_context = create_context
884
+
885
+ for entity, custom_handler in custom_ids do
886
+ process(-(entity :: any), custom_handler)
887
+ end
888
+
889
+ for entity, command_buffer in command_buffers do
890
+ if (entity :: any) < 0 then
891
+ continue
892
+ end
893
+ client_replicator.apply_command_buffer(client, entity, command_buffer, process)
894
+ end
895
+ end
896
+
897
+ function client_replicator.apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
437
898
  local c = cursor.from(buf)
438
899
  check_packet_type(c, "reliable")
439
- resolve_dirty(client)
900
+ client_replicator.resolve_dirty(client)
440
901
  client.is_replicating = true
441
902
 
442
903
  local components = client.components
443
904
  local total_packets = cursor.read_vlq(c)
444
905
  local variant_start = (all_variants and #all_variants + 1) :: number
906
+ local changed_hooks = client.network_hooks.changed
907
+ local removed_hooks = client.network_hooks.removed
908
+ local deleted_hooks = client.network_hooks.deleted
909
+
910
+ local command_buffers: { [Entity]: CommandBuffer } = {}
911
+ local custom_ids: CustomIdMap = {}
912
+
913
+ client.command_buffers = command_buffers
445
914
 
446
915
  for a = 1, total_packets do
447
916
  local variants = all_variants and all_variants[variant_start - a]
@@ -449,154 +918,152 @@ local function apply_updates(client: Client, buf: buffer, all_variants: { { any
449
918
 
450
919
  local total_added = read_vlq_bitmask(c, storage_mask, 0)
451
920
  for _ = 1, total_added do
452
- process_entity(client, c, variants)
921
+ client_replicator.process_entity(client, c, custom_ids, variants)
453
922
  end
454
923
 
455
924
  local total_components_added = read_vlq_bitmask(c, storage_mask, 1)
456
925
  for _ = 1, total_components_added do
457
- local entity = process_entity_id(client, c, variants)
458
- local added_mask = cursor.readu8(c)
459
-
460
- local entity_hooks = client.entity_hooks[entity]
461
- local changed_hooks = entity_hooks and entity_hooks.changed
462
-
463
- local total_tags = read_vlq_bitmask(c, added_mask, 0)
464
- for _ = 1, total_tags do
465
- local tag = read_component_id(client, c)
466
- local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
467
- network_entity_add(client, entity, tag, tag_hooks)
468
- end
926
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
927
+ local added_mask = cursor.readu8(c)
928
+
929
+ local total_tags = read_vlq_bitmask(c, added_mask, 0)
930
+ for _ = 1, total_tags do
931
+ local tag = client_replicator.read_component_id(client, c)
932
+ local tag_hooks = changed_hooks[PAIR(components.reliable, tag)]
933
+ client_replicator.entity_add(client, entity, tag, tag_hooks)
934
+ end
469
935
 
470
- local total_components = read_vlq_bitmask(c, added_mask, 1)
471
- for _ = 1, total_components do
472
- local component, value = read_component(client, c, variants)
473
- local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
474
- network_entity_set(client, entity, component, value, component_hooks)
475
- end
936
+ local total_components = read_vlq_bitmask(c, added_mask, 1)
937
+ for _ = 1, total_components do
938
+ local component, value = client_replicator.read_component(client, c, variants)
939
+ local component_hooks = changed_hooks[PAIR(components.reliable, component)]
940
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
941
+ end
476
942
 
477
- local total_pairs = read_vlq_bitmask(c, added_mask, 2)
478
- for _ = 1, total_pairs do
479
- process_entity_relations(client, entity, changed_hooks, c, variants)
480
- end
943
+ local total_pairs = read_vlq_bitmask(c, added_mask, 2)
944
+ for _ = 1, total_pairs do
945
+ client_replicator.process_entity_relations(client, entity, c, custom_ids)
946
+ end
481
947
 
482
- local total_pairs_values = read_vlq_bitmask(c, added_mask, 3)
483
- for _ = 1, total_pairs_values do
484
- process_entity_relation_values(client, entity, changed_hooks, c, variants)
485
- end
948
+ local total_pairs_values = read_vlq_bitmask(c, added_mask, 3)
949
+ for _ = 1, total_pairs_values do
950
+ client_replicator.process_entity_relation_values(client, entity, c, custom_ids, variants)
951
+ end
952
+ end)
486
953
  end
487
954
 
488
955
  local total_changed = read_vlq_bitmask(c, storage_mask, 2)
489
956
  for _ = 1, total_changed do
490
- local entity = process_entity_id(client, c, variants)
491
- local changed_mask = cursor.readu8(c)
492
-
493
- local entity_hooks = client.entity_hooks[entity]
494
- local changed_hooks = entity_hooks and entity_hooks.changed
495
- local removed_hooks = entity_hooks and entity_hooks.removed
496
-
497
- local total_tagged = read_vlq_bitmask(c, changed_mask, 0)
498
- for _ = 1, total_tagged do
499
- local tag = read_component_id(client, c)
500
- local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
501
- network_entity_add(client, entity, tag, tag_hooks)
502
- end
503
-
504
- local total_components = read_vlq_bitmask(c, changed_mask, 1)
505
- for _ = 1, total_components do
506
- local component, value = read_component(client, c, variants)
507
- local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
508
- network_entity_set(client, entity, component, value, component_hooks)
509
- end
510
-
511
- local total_removed = read_vlq_bitmask(c, changed_mask, 2)
512
- for _ = 1, total_removed do
513
- local component = read_component_id(client, c)
514
- local component_hooks = removed_hooks and removed_hooks[PAIR(components.reliable, component)]
515
- network_entity_remove(client, entity, component, component_hooks)
516
- end
517
-
518
- local total_pairs = read_vlq_bitmask(c, changed_mask, 3)
519
- for _ = 1, total_pairs do
520
- local relation = read_component_id(client, c)
521
- local pair_changed_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
522
- local pair_removed_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
523
- local total_targets = cursor.read_vlq(c)
524
-
525
- for _ = 1, total_targets do
526
- local action_type = cursor.readi8(c)
527
-
528
- if action_type == 1 then -- add pair
529
- local target = process_entity_id(client, c, variants)
530
- network_entity_add(client, entity, PAIR(relation, target), pair_changed_hooks)
531
- elseif action_type == -1 then -- remove pair
532
- local target = read_entity_id(client, c, variants)
533
- if target then
534
- network_entity_remove(client, entity, PAIR(relation, target), pair_removed_hooks)
957
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
958
+ local changed_mask = cursor.readu8(c)
959
+
960
+ local total_tagged = read_vlq_bitmask(c, changed_mask, 0)
961
+ for _ = 1, total_tagged do
962
+ local tag = client_replicator.read_component_id(client, c)
963
+ local tag_hooks = changed_hooks[PAIR(components.reliable, tag)]
964
+ client_replicator.entity_add(client, entity, tag, tag_hooks)
965
+ end
966
+
967
+ local total_components = read_vlq_bitmask(c, changed_mask, 1)
968
+ for _ = 1, total_components do
969
+ local component, value = client_replicator.read_component(client, c, variants)
970
+ local component_hooks = changed_hooks[PAIR(components.reliable, component)]
971
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
972
+ end
973
+
974
+ local total_removed = read_vlq_bitmask(c, changed_mask, 2)
975
+ for _ = 1, total_removed do
976
+ local component = client_replicator.read_component_id(client, c)
977
+ local component_hooks = removed_hooks[PAIR(components.reliable, component)]
978
+ client_replicator.entity_remove(client, entity, component, component_hooks)
979
+ end
980
+
981
+ local total_pairs = read_vlq_bitmask(c, changed_mask, 3)
982
+ for _ = 1, total_pairs do
983
+ local relation = client_replicator.read_component_id(client, c)
984
+ local pair_changed_hooks = changed_hooks[PAIR(components.pair, relation)]
985
+ local pair_removed_hooks = removed_hooks[PAIR(components.pair, relation)]
986
+ local total_targets = cursor.read_vlq(c)
987
+
988
+ for _ = 1, total_targets do
989
+ local action_type = cursor.readi8(c)
990
+
991
+ if action_type == 1 then -- add pair
992
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
993
+ client_replicator.entity_add_pair(client, entity, relation, target, pair_changed_hooks)
994
+ end)
995
+ elseif action_type == -1 then -- remove pair
996
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
997
+ client_replicator.entity_remove_pair(
998
+ client,
999
+ entity,
1000
+ relation,
1001
+ target,
1002
+ pair_removed_hooks
1003
+ )
1004
+ end)
535
1005
  end
536
1006
  end
537
1007
  end
538
- end
539
1008
 
540
- local total_pairs_values_removed = read_vlq_bitmask(c, changed_mask, 4)
541
- for _ = 1, total_pairs_values_removed do
542
- local relation = read_component_id(client, c)
543
- local pair_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
544
- local total_targets = cursor.read_vlq(c)
1009
+ local total_pairs_values_removed = read_vlq_bitmask(c, changed_mask, 4)
1010
+ for _ = 1, total_pairs_values_removed do
1011
+ local relation = client_replicator.read_component_id(client, c)
1012
+ local pair_hooks = removed_hooks[PAIR(components.pair, relation)]
1013
+ local total_targets = cursor.read_vlq(c)
545
1014
 
546
- for _ = 1, total_targets do
547
- local target = read_entity_id(client, c, variants)
548
- if target then
549
- network_entity_remove(client, entity, PAIR(relation, target), pair_hooks)
1015
+ for _ = 1, total_targets do
1016
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
1017
+ client_replicator.entity_remove_pair(client, entity, relation, target, pair_hooks)
1018
+ end)
550
1019
  end
551
1020
  end
552
- end
553
-
554
- local total_pairs_values = read_vlq_bitmask(c, changed_mask, 5)
555
- for _ = 1, total_pairs_values do
556
- local relation = read_component_id(client, c)
557
- local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
558
- local total_targets = cursor.read_vlq(c)
559
1021
 
560
- for _ = 1, total_targets do
561
- local value = read_component_value(client, relation, c, variants)
562
- local target = process_entity_id(client, c, variants)
563
- network_entity_set(client, entity, PAIR(relation, target), value, pair_hooks)
1022
+ local total_pairs_values = read_vlq_bitmask(c, changed_mask, 5)
1023
+ for _ = 1, total_pairs_values do
1024
+ local relation = client_replicator.read_component_id(client, c)
1025
+ local pair_hooks = changed_hooks[PAIR(components.pair, relation)]
1026
+ local total_targets = cursor.read_vlq(c)
1027
+
1028
+ for _ = 1, total_targets do
1029
+ local value = client_replicator.read_component_value(client, relation, c, variants)
1030
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
1031
+ client_replicator.entity_set_pair(client, entity, relation, target, value, pair_hooks)
1032
+ end)
1033
+ end
564
1034
  end
565
- end
1035
+ end)
566
1036
  end
567
1037
 
568
1038
  local total_component_deletions = read_vlq_bitmask(c, storage_mask, 3)
569
1039
  for _ = 1, total_component_deletions do
570
- local entity = process_entity_id(client, c, variants)
571
- local entity_hooks = client.entity_hooks[entity]
572
- local removed_hooks = entity_hooks and entity_hooks.removed
573
-
574
- local total_removed = cursor.read_vlq(c)
575
- for _ = 1, total_removed do
576
- local component = read_component_id(client, c)
577
- local component_hooks = removed_hooks and removed_hooks[PAIR(components.reliable, component)]
578
- network_entity_remove(client, entity, component, component_hooks)
579
- end
1040
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
1041
+ local total_removed = cursor.read_vlq(c)
1042
+ for _ = 1, total_removed do
1043
+ local component = client_replicator.read_component_id(client, c)
1044
+ local component_hooks = removed_hooks[PAIR(components.reliable, component)]
1045
+ client_replicator.entity_remove(client, entity, component, component_hooks)
1046
+ end
580
1047
 
581
- local total_pairs = cursor.read_vlq(c)
582
- for _ = 1, total_pairs do
583
- local relation = read_component_id(client, c)
584
- local pair_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
585
- network_entity_remove_relations(client, entity, relation, pair_hooks)
586
- end
1048
+ local total_pairs = cursor.read_vlq(c)
1049
+ for _ = 1, total_pairs do
1050
+ local relation = client_replicator.read_component_id(client, c)
1051
+ local pair_hooks = removed_hooks[PAIR(components.pair, relation)]
1052
+ client_replicator.entity_remove_relations(client, entity, relation, pair_hooks)
1053
+ end
1054
+ end)
587
1055
  end
588
1056
 
589
1057
  local total_deleted = read_vlq_bitmask(c, storage_mask, 4)
590
1058
  for _ = 1, total_deleted do
591
- local entity, server_id = read_entity_id(client, c, variants)
1059
+ local entity, server_id = client_replicator.read_entity_id(client, c)
592
1060
  if entity then
593
- local entity_hooks = client.entity_hooks[entity]
594
- local deleted_hooks = entity_hooks and entity_hooks.deleted
595
- if deleted_hooks then
596
- for _, callback in deleted_hooks.callbacks do
1061
+ local deleted_hook = deleted_hooks[entity]
1062
+ if deleted_hook then
1063
+ for _, callback in deleted_hook.callbacks do
597
1064
  callback(entity)
598
1065
  end
599
- if not deleted_hooks.overrides then
1066
+ if not deleted_hook.overrides then
600
1067
  client.world:delete(entity)
601
1068
  end
602
1069
  else
@@ -609,72 +1076,76 @@ local function apply_updates(client: Client, buf: buffer, all_variants: { { any
609
1076
  end
610
1077
  end
611
1078
 
612
- finish_replication(client)
1079
+ client_replicator.finish_custom_ids(client, custom_ids, command_buffers)
1080
+ client_replicator.finish_replication(client)
613
1081
  end
614
1082
 
615
- local function apply_unreliable(client: Client, buf: buffer, all_variants: { { any } }?)
1083
+ function client_replicator.apply_unreliable(client: Client, buf: buffer, all_variants: { { any } }?)
616
1084
  local c = cursor.from(buf)
617
1085
  check_packet_type(c, "unreliable")
618
- resolve_dirty(client)
1086
+ client_replicator.resolve_dirty(client)
619
1087
  client.is_replicating = true
620
1088
 
621
1089
  local components = client.components
622
1090
  local total_packets = cursor.read_vlq(c)
623
1091
  local variant_start = (all_variants and #all_variants + 1) :: number
1092
+ local changed_hooks = client.network_hooks.changed
624
1093
 
625
1094
  for a = 1, total_packets do
626
1095
  local total_entities = cursor.read_vlq(c)
627
1096
  local variants = all_variants and all_variants[variant_start - a]
628
1097
 
629
1098
  for _ = 1, total_entities do
630
- local entity = read_entity_id(client, c, variants)
631
-
1099
+ local entity = client_replicator.read_entity_id(client, c)
632
1100
  local total_unreliable = cursor.read_vlq(c)
633
1101
 
634
1102
  if entity then
635
- local entity_hooks = client.entity_hooks[entity]
636
- local changed_hooks = entity_hooks and entity_hooks.changed
637
-
638
1103
  for _ = 1, total_unreliable do
639
- local component_id, value = read_component(client, c, variants)
640
- local unreliable_hooks = changed_hooks and changed_hooks[PAIR(components.unreliable, component_id)]
641
- network_entity_set(client, entity, component_id, value, unreliable_hooks)
1104
+ local component_id, value = client_replicator.read_component(client, c, variants)
1105
+ local unreliable_hooks = changed_hooks[PAIR(components.unreliable, component_id)]
1106
+ client_replicator.entity_set_unreliable(client, entity, component_id, value, unreliable_hooks)
642
1107
  end
643
1108
  else
644
1109
  -- dont apply unreliable updates to entities that dont exist in the client yet
645
1110
  -- but we still need to process the values to keep the cursor in the right place
646
1111
  for _ = 1, total_unreliable do
647
- read_component(client, c, variants)
1112
+ client_replicator.read_component(client, c, variants)
648
1113
  end
649
1114
  end
650
1115
  end
651
1116
  end
652
1117
 
653
- finish_replication(client)
1118
+ client_replicator.finish_replication(client)
654
1119
  end
655
1120
 
656
- local function apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
1121
+ function client_replicator.apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
657
1122
  local c = cursor.from(buf)
658
1123
  check_packet_type(c, "full")
659
- resolve_dirty(client)
1124
+ client_replicator.resolve_dirty(client)
660
1125
  client.is_replicating = true
661
1126
 
662
1127
  local total_packets = cursor.read_vlq(c)
663
1128
  local variant_start = (all_variants and #all_variants + 1) :: number
664
1129
 
1130
+ local command_buffers: { [Entity]: CommandBuffer } = {}
1131
+ local custom_ids: CustomIdMap = {}
1132
+
1133
+ client.command_buffers = command_buffers
1134
+
665
1135
  for a = 1, total_packets do
666
1136
  local total_entities = cursor.read_vlq(c)
667
1137
  local variants = all_variants and all_variants[variant_start - a]
668
1138
 
669
1139
  for _ = 1, total_entities do
670
- process_entity(client, c, variants)
1140
+ client_replicator.process_entity(client, c, custom_ids, variants)
671
1141
  end
672
1142
  end
673
1143
 
674
- finish_replication(client)
1144
+ client_replicator.finish_custom_ids(client, custom_ids, command_buffers)
1145
+ client_replicator.finish_replication(client)
675
1146
  end
676
1147
 
677
- local function init(client: Client, _world: World?)
1148
+ function client_replicator.init(client: Client, _world: World?)
678
1149
  if client.inited == true then
679
1150
  return warn "attempted to init a client twice"
680
1151
  end
@@ -699,11 +1170,7 @@ local function init(client: Client, _world: World?)
699
1170
 
700
1171
  local components = client.components
701
1172
 
702
- client.shared = utils.create_shared_lookup(world, components)
703
-
704
- for component, custom_get in world:query(components.custom_handler):with(components.shared, ECS_NAME) do
705
- client.custom_ids[component] = custom_get
706
- end
1173
+ client.shared = utils.resolved_shared(world, components, client.registered_custom_ids)
707
1174
 
708
1175
  local alive_unhook = hooks.removed(world, components.__alive_tracking__, function(entity)
709
1176
  local server_id = client.client_ids[entity]
@@ -711,23 +1178,23 @@ local function init(client: Client, _world: World?)
711
1178
  client.server_ids[server_id] = nil
712
1179
  client.client_ids[entity] = nil
713
1180
  end
714
- client.entity_hooks[entity] = nil
1181
+ client.network_hooks.deleted[entity] = nil
715
1182
  end)
716
1183
  table.insert(client.hooked, alive_unhook)
717
1184
 
718
1185
  for _, component in client.requires_shared_lookup do
719
1186
  local added_hook = hooks.added(world, component, function()
720
- client.is_dirty = true
1187
+ client.is_shared_dirty = true
721
1188
  end)
722
1189
  local removed_hook = hooks.removed(world, component, function()
723
- client.is_dirty = true
1190
+ client.is_shared_dirty = true
724
1191
  end)
725
1192
  table.insert(client.hooked, added_hook)
726
1193
  table.insert(client.hooked, removed_hook)
727
1194
  end
728
1195
  end
729
1196
 
730
- local function after_replication(client: Client, callback: () -> ())
1197
+ function client_replicator.after_replication(client: Client, callback: () -> ())
731
1198
  if client.is_replicating then
732
1199
  table.insert(client.after_replication_callbacks, callback)
733
1200
  else
@@ -735,7 +1202,7 @@ local function after_replication(client: Client, callback: () -> ())
735
1202
  end
736
1203
  end
737
1204
 
738
- local function added(client: Client, callback: (entity: Entity) -> ())
1205
+ function client_replicator.added(client: Client, callback: (entity: Entity) -> ())
739
1206
  table.insert(client.addition_hooks, callback)
740
1207
  return function()
741
1208
  local index = table.find(client.addition_hooks, callback)
@@ -745,24 +1212,11 @@ local function added(client: Client, callback: (entity: Entity) -> ())
745
1212
  end
746
1213
  end
747
1214
 
748
- local function get_or_create_entity_hooks(client: Client, entity: Entity)
749
- local entity_hooks = client.entity_hooks[entity]
750
- if not entity_hooks then
751
- entity_hooks = {
752
- changed = {},
753
- removed = {},
754
- deleted = nil :: DeletedHookEntry?,
755
- }
756
- client.entity_hooks[entity] = entity_hooks
757
- end
758
- return entity_hooks
759
- end
760
-
761
- local function add_hook_entry(client: Client, entity: Entity, action: string, ...): ({ overrides: boolean }, () -> ())
1215
+ local function add_hook_entry(client: Client, action: string, ...): ({ overrides: boolean }, () -> ())
762
1216
  if action == "changed" or action == "removed" then
763
1217
  local relation = select(1, ...) :: Entity
764
1218
  local callback = select(2, ...) :: (entity: Entity, id: number, value: any) -> ()
765
- local hooks = get_or_create_entity_hooks(client, entity)[action]
1219
+ local hooks = action == "changed" and client.network_hooks.changed or client.network_hooks.removed
766
1220
  local entries = hooks[relation] :: ChangedHooksEntry
767
1221
 
768
1222
  if not entries then
@@ -786,16 +1240,17 @@ local function add_hook_entry(client: Client, entity: Entity, action: string, ..
786
1240
  end
787
1241
  return entries, disconnect
788
1242
  elseif action == "deleted" then
789
- local callback = select(1, ...) :: (entity: Entity) -> ()
790
- local hooks = get_or_create_entity_hooks(client, entity)
791
- local entries = hooks.deleted :: DeletedHookEntry
1243
+ local entity = select(1, ...) :: Entity
1244
+ local callback = select(2, ...) :: (entity: Entity) -> ()
1245
+ local hooks = client.network_hooks.deleted
1246
+ local entries = hooks[entity]
792
1247
 
793
1248
  if not entries then
794
1249
  entries = {
795
1250
  overrides = false,
796
1251
  callbacks = {},
797
1252
  }
798
- hooks.deleted = entries
1253
+ hooks[entity] = entries
799
1254
  end
800
1255
  table.insert(entries.callbacks, callback)
801
1256
 
@@ -805,7 +1260,7 @@ local function add_hook_entry(client: Client, entity: Entity, action: string, ..
805
1260
  if index then
806
1261
  table.remove(callbacks, index)
807
1262
  if #callbacks == 0 then
808
- hooks.deleted = nil
1263
+ hooks[entity] = nil
809
1264
  end
810
1265
  end
811
1266
  end
@@ -815,26 +1270,55 @@ local function add_hook_entry(client: Client, entity: Entity, action: string, ..
815
1270
  end
816
1271
  end
817
1272
 
818
- local function hook(client: Client, entity: Entity, action: string, ...)
819
- local entry, disconnect = add_hook_entry(client, entity, action, ...)
1273
+ function client_replicator.hook(client: Client, action: string, ...)
1274
+ local entry, disconnect = add_hook_entry(client, action, ...)
820
1275
  entry.overrides = false
821
1276
  return disconnect
822
1277
  end
823
- local function override(client: Client, entity: Entity, action: string, ...)
824
- local entry, disconnect = add_hook_entry(client, entity, action, ...)
1278
+ function client_replicator.override(client: Client, action: string, ...)
1279
+ local entry, disconnect = add_hook_entry(client, action, ...)
825
1280
  entry.overrides = true
826
1281
  return disconnect
827
1282
  end
828
1283
 
829
- local function get_server_entity(client: Client, client_entity: Entity): number?
1284
+ function client_replicator.get_server_entity(client: Client, client_entity: Entity): number?
830
1285
  return client.client_ids[client_entity]
831
1286
  end
832
1287
 
833
- local function get_client_entity(client: Client, client_entity: number): Entity?
1288
+ function client_replicator.get_client_entity(client: Client, client_entity: number): Entity?
834
1289
  return client.server_ids[client_entity]
835
1290
  end
836
1291
 
837
- local function destroy(client: Client)
1292
+ function client_replicator.register_entity(client: Client, entity: Entity, server_entity: number)
1293
+ client.server_ids[server_entity] = entity
1294
+ client.client_ids[entity] = server_entity
1295
+ client.world:add(entity, client.components.__alive_tracking__)
1296
+ end
1297
+
1298
+ function client_replicator.unregister_entity(client: Client, entity: Entity)
1299
+ local server_id = client.client_ids[entity]
1300
+ if server_id then
1301
+ client.server_ids[server_id] = nil
1302
+ client.client_ids[entity] = nil
1303
+ end
1304
+ end
1305
+
1306
+ function client_replicator.register_custom_id(client: Client, custom_id: CustomId)
1307
+ client.registered_custom_ids[custom_id] = true
1308
+ client.is_shared_dirty = true
1309
+ end
1310
+
1311
+ function client_replicator.generate_handshake(client: Client): common.HandshakeInfo
1312
+ client_replicator.resolve_dirty(client)
1313
+ return utils.generate_handshake(client.world, client.shared)
1314
+ end
1315
+
1316
+ function client_replicator.verify_handshake(client: Client, handshake: common.HandshakeInfo): (boolean, string?)
1317
+ client_replicator.resolve_dirty(client)
1318
+ return utils.verify_handshake(client.world, client.shared, handshake, "server", "client")
1319
+ end
1320
+
1321
+ function client_replicator.destroy(client: Client)
838
1322
  if client.inited == nil then
839
1323
  return warn "attempted to destroy a client twice"
840
1324
  end
@@ -844,40 +1328,21 @@ local function destroy(client: Client)
844
1328
  end
845
1329
  end
846
1330
 
847
- local function handle_global(client: Client, handler: (id: number) -> Entity)
1331
+ function client_replicator.handle_global(client: Client, handler: (id: number) -> Entity)
848
1332
  client.global_handler = handler
849
1333
  end
850
1334
 
851
- local client = {}
852
- client.__index = client
853
- client.init = init
854
- client.destroy = destroy
855
- client.apply_updates = apply_updates
856
- client.apply_unreliable = apply_unreliable
857
- client.apply_full = apply_full
858
- client.encode_component = encode_component
859
- client.decode_component = decode_component
860
- client.get_server_entity = get_server_entity
861
- client.get_client_entity = get_client_entity
862
- client.handle_global = handle_global
863
- client.after_replication = after_replication
864
- client.hook = hook
865
- client.override = override
866
- client.added = added
867
-
868
1335
  local function create(world: World?, components: common.Components): Client
869
1336
  local self = {} :: Client
870
1337
 
871
1338
  self.components = components
872
1339
  self.ordered_creation = true
873
- self.custom_ids = {}
874
1340
  self.server_ids = {}
875
1341
  self.client_ids = {}
876
- self.is_dirty = false
1342
+ self.is_shared_dirty = false
877
1343
  self.requires_shared_lookup = {
878
1344
  self.components.shared,
879
1345
  self.components.serdes,
880
- self.components.bytespan,
881
1346
  jecs.Name,
882
1347
  }
883
1348
  self.global_handler = nil
@@ -885,14 +1350,20 @@ local function create(world: World?, components: common.Components): Client
885
1350
  self.inited = false
886
1351
  self.is_replicating = false
887
1352
  self.hooked = {}
1353
+ self.registered_custom_ids = {}
888
1354
 
889
1355
  self.addition_hooks = {}
890
- self.entity_hooks = {}
1356
+ self.network_hooks = {
1357
+ deleted = {},
1358
+ changed = {},
1359
+ removed = {},
1360
+ }
891
1361
  self.after_replication_callbacks = {}
892
1362
 
893
- return setmetatable(self, client) :: any
1363
+ return setmetatable(self, client_replicator) :: any
894
1364
  end
895
1365
 
896
- client.create = create
897
-
898
- return client :: { create: typeof(create) }
1366
+ return {
1367
+ create = create,
1368
+ client_replicator = client_replicator,
1369
+ }