@rbxts/replecs 0.2.3 → 0.2.4

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.
@@ -1,19 +1,19 @@
1
- --!native
2
1
  --!optimize 2
2
+ --!native
3
3
 
4
- local jecs = require "../jecs"
5
- local common = require "./common"
6
- local customid = require "./customid"
7
- local utils = require "./utils"
4
+ local common = require (script.Parent:WaitForChild('common'))
5
+ local types = require (script.Parent:WaitForChild('types'))
6
+ local customid = require (script.Parent:WaitForChild('customid'))
7
+ local utils = require (script.Parent:WaitForChild('utils'))
8
8
 
9
9
  local cursor = utils.cursor
10
10
 
11
11
  type Cursor = utils.Cursor
12
12
  type CustomId = customid.CustomId
13
13
 
14
- type Entity<T = any> = jecs.Entity<T>
15
- type Id<T = any> = jecs.Id<T>
16
- type World = jecs.World
14
+ type Entity = common.Entity
15
+ type Component<T = any> = common.Component<T>
16
+ type World = common.World
17
17
 
18
18
  type Array<T> = { T }
19
19
  type Map<K, V> = { [K]: V }
@@ -21,33 +21,46 @@ type Set<T> = { [T]: boolean }
21
21
  type Disconnect = () -> ()
22
22
 
23
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>,
24
+ tags: Set<Component>,
25
+ components: Map<Component, { value: any }>,
26
+ unreliable_components: Map<Component, { value: any }>,
27
+ remove: Set<Component>,
28
+
29
+ pairs_add: { [Component]: Set<Entity> },
30
+ pairs_set: { [Component]: Map<Entity, { value: any }> },
31
+ pairs_unprocessed: {
32
+ [Component]: Map<Entity, {
33
+ cursor: Cursor,
34
+ variants: { any }?,
35
+ processed_value: { value: any }?,
36
+ }>,
37
+ },
38
+ pairs_remove: { [Component]: Set<Entity> },
39
+ relations_clear: Set<Component>,
32
40
  }
33
41
 
34
42
  type HookMethod =
35
43
  & ((
36
44
  self: Client,
37
45
  action: "changed",
38
- relation: Id,
39
- callback: (entity: Entity, id: Id, value: any) -> ()
46
+ relation: Component,
47
+ callback: (entity: Entity, id: Component, value: any) -> ()
40
48
  ) -> 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
43
-
49
+ & ((
50
+ self: Client,
51
+ action: "removed",
52
+ relation: Component,
53
+ callback: (entity: Entity, id: Component) -> ()
54
+ ) -> Disconnect)
55
+ & ((self: Client, action: "deleted", entity: Entity, callback: (entity: Entity) -> ()) -> Disconnect
56
+ )
44
57
  type ChangedHooksEntry = {
45
58
  overrides: boolean,
46
- callbacks: Array<(entity: Entity, id: Id, value: any) -> ()>,
59
+ callbacks: Array<(entity: Entity, id: Component, value: any) -> ()>,
47
60
  }
48
61
  type RemovedHooksEntry = {
49
62
  overrides: boolean,
50
- callbacks: Array<(entity: Entity, id: Id) -> ()>,
63
+ callbacks: Array<(entity: Entity, id: Component) -> ()>,
51
64
  }
52
65
  type DeletedHookEntry = {
53
66
  overrides: boolean,
@@ -56,7 +69,12 @@ type DeletedHookEntry = {
56
69
 
57
70
  type CustomIdMap = { [Entity]: CustomId | Entity }
58
71
 
59
- export type Client = {
72
+ export type ClientImp = {
73
+ set_serdes: <T>(client: Client, id: Component<T>, serdes: types.Serdes<T>) -> (),
74
+ remove_serdes: (client: Client, id: Component) -> (),
75
+ }
76
+
77
+ export type Client = ClientImp & {
60
78
  world: World,
61
79
  inited: boolean?,
62
80
 
@@ -65,16 +83,17 @@ export type Client = {
65
83
 
66
84
  is_shared_dirty: boolean,
67
85
  requires_shared_lookup: { Entity },
68
- components: common.Components,
69
- shared: common.Shared,
86
+ components: types.Components,
87
+ shared: types.Shared,
70
88
  global_handler: ((id: number) -> Entity)?,
71
89
  server_ids: { [number]: Entity },
72
90
  client_ids: { [Entity]: number },
73
- ordered_creation: boolean,
74
- registered_custom_ids: { [CustomId]: boolean },
75
91
  command_buffers: { [Entity]: CommandBuffer }?,
76
92
  command_buffers_active: boolean,
77
93
 
94
+ registered_custom_ids: { [CustomId]: boolean },
95
+ component_serdes: Map<Component, types.Serdes>,
96
+
78
97
  init: (self: Client, world: World?) -> (),
79
98
  destroy: (self: Client) -> (),
80
99
  handle_global: (self: Client, handler: (id: number) -> Entity) -> (),
@@ -84,7 +103,6 @@ export type Client = {
84
103
  register_entity: (self: Client, entity: Entity, server_entity: number) -> (),
85
104
  unregister_entity: (self: Client, entity: Entity) -> (),
86
105
 
87
- hooks: common.WorldHooks,
88
106
  hooked: Array<() -> ()>,
89
107
 
90
108
  after_replication: (self: Client, callback: () -> ()) -> (),
@@ -94,20 +112,23 @@ export type Client = {
94
112
  addition_hooks: Array<(entity: Entity) -> ()>,
95
113
  network_hooks: {
96
114
  deleted: { [Entity]: DeletedHookEntry },
97
- changed: { [Id]: ChangedHooksEntry },
98
- removed: { [Id]: RemovedHooksEntry },
115
+ changed: { [Component]: ChangedHooksEntry },
116
+ removed: { [Component]: RemovedHooksEntry },
99
117
  },
100
118
 
101
119
  encode_component: (self: Client, component: Entity) -> number,
102
120
  decode_component: (self: Client, component: number) -> Entity,
121
+ get_shared_count: (self: Client) -> number,
122
+
103
123
  register_custom_id: (self: Client, custom_id: CustomId) -> (),
104
124
 
125
+ apply_full: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
126
+ apply_entity: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
105
127
  apply_updates: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
106
128
  apply_unreliable: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
107
- apply_full: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
108
129
 
109
- generate_handshake: (self: Client) -> common.HandshakeInfo,
110
- verify_handshake: (self: Client, handshake: common.HandshakeInfo) -> (boolean, string?),
130
+ generate_handshake: (self: Client) -> types.HandshakeInfo,
131
+ verify_handshake: (self: Client, handshake: types.HandshakeInfo) -> (boolean, string?),
111
132
  }
112
133
 
113
134
  local client_replicator = {}
@@ -123,23 +144,41 @@ local ENTITY_ID_TYPES = {
123
144
  }
124
145
  local PACKET_TYPES = {
125
146
  full = 1,
126
- unreliable = 2,
127
- reliable = 3,
147
+ entity = 2,
148
+ updates = 3,
149
+ unreliable = 4,
128
150
  }
129
151
 
130
- local WILDCARD = jecs.Wildcard
131
-
132
- local function PAIR(first: Entity, second: Entity): Entity
133
- return jecs.pair(first, second)
152
+ local ECS_NAME = common.ecs_name
153
+ local ECS_WILDCARD = common.wildcard
154
+ local PAIR = common.pair
155
+ local ITERATE_ALL_RELATIONS = common.iterate_all_relations
156
+ local WORLD_ENTITY = common.world_entity
157
+ local WORLD_ADD = common.world_add
158
+ local WORLD_SET = common.world_set
159
+ local WORLD_REMOVE = common.world_remove
160
+ local WORLD_GET = common.world_get
161
+ local WORLD_DELETE = common.world_delete
162
+
163
+ local HOOK_ADDED = common.hook_added
164
+ local HOOK_CHANGED = common.hook_changed
165
+ local HOOK_REMOVED = common.hook_removed
166
+
167
+ local function QUERY_CURRENT_COMPONENTS(client: Client)
168
+ for component, serdes in client.world:query(client.components.serdes):iter() do
169
+ client.component_serdes[component] = serdes
170
+ end
134
171
  end
135
172
 
136
173
  local function get_or_create_entity(client: Client, server_id: number): Entity
174
+ local world = client.world
175
+
137
176
  local server_entity = client.server_ids[server_id]
138
177
  if server_entity then
139
178
  return server_entity
140
179
  else
141
- local entity = client.world:entity()
142
- client.world:add(entity, client.components.__alive_tracking__)
180
+ local entity = WORLD_ENTITY(world)
181
+ WORLD_ADD(world, entity, client.components.__alive_tracking__)
143
182
 
144
183
  client.server_ids[server_id] = entity
145
184
  client.client_ids[entity] = server_id
@@ -152,11 +191,11 @@ local function get_or_create_entity(client: Client, server_id: number): Entity
152
191
  end
153
192
  end
154
193
 
155
- local function get_command_buffer(client: Client, entity: Entity): CommandBuffer
194
+ local function get_or_create_command_buffer(client: Client, entity: Entity): CommandBuffer
156
195
  local command_buffers = client.command_buffers
157
196
 
158
197
  if not command_buffers then
159
- error "attempted to use a command buffer without having one. (this should never happen)"
198
+ error "attempted to use a command buffer without being active. (this should never happen)"
160
199
  end
161
200
 
162
201
  local command_buffer = command_buffers[entity :: any]
@@ -166,10 +205,11 @@ local function get_command_buffer(client: Client, entity: Entity): CommandBuffer
166
205
  components = {},
167
206
  unreliable_components = {},
168
207
  remove = {},
169
- pairs = {},
170
- pairs_values = {},
208
+ pairs_add = {},
209
+ pairs_set = {},
210
+ pairs_unprocessed = {},
171
211
  pairs_remove = {},
172
- pairs_clear = {},
212
+ relations_clear = {},
173
213
  }
174
214
  command_buffers[entity :: any] = command_buffer
175
215
  end
@@ -179,41 +219,41 @@ end
179
219
  function client_replicator.entity_set(
180
220
  client: Client,
181
221
  entity: Entity,
182
- id: Id,
222
+ id: Component,
183
223
  value: any,
184
224
  changed_hooks: ChangedHooksEntry?
185
225
  )
186
226
  if client.command_buffers_active then
187
- local command_buffer = get_command_buffer(client, entity)
227
+ local command_buffer = get_or_create_command_buffer(client, entity)
188
228
  command_buffer.components[id] = { value = value }
189
229
  else
190
230
  if changed_hooks then
191
231
  if not changed_hooks.overrides then
192
- client.world:set(entity, id, value)
232
+ WORLD_SET(client.world, entity, id, value)
193
233
  end
194
234
  for _, callback in changed_hooks.callbacks do
195
235
  callback(entity, id, value)
196
236
  end
197
237
  else
198
- client.world:set(entity, id, value)
238
+ WORLD_SET(client.world, entity, id, value)
199
239
  end
200
240
  end
201
241
  end
202
242
 
203
243
  function client_replicator.entity_add(client: Client, entity: Entity, tag: Entity, changed_hooks: ChangedHooksEntry?)
204
244
  if client.command_buffers_active then
205
- local command_buffer = get_command_buffer(client, entity)
245
+ local command_buffer = get_or_create_command_buffer(client, entity)
206
246
  command_buffer.tags[tag] = true
207
247
  else
208
248
  if changed_hooks then
209
249
  if not changed_hooks.overrides then
210
- client.world:add(entity, tag)
250
+ WORLD_ADD(client.world, entity, tag)
211
251
  end
212
252
  for _, callback in changed_hooks.callbacks do
213
253
  callback(entity, tag)
214
254
  end
215
255
  else
216
- client.world:add(entity, tag)
256
+ WORLD_ADD(client.world, entity, tag)
217
257
  end
218
258
  end
219
259
  end
@@ -226,7 +266,7 @@ function client_replicator.entity_set_unreliable(
226
266
  changed_hooks: ChangedHooksEntry?
227
267
  )
228
268
  if client.command_buffers_active then
229
- local command_buffer = get_command_buffer(client, entity)
269
+ local command_buffer = get_or_create_command_buffer(client, entity)
230
270
  command_buffer.unreliable_components[id] = { value = value }
231
271
  else
232
272
  client_replicator.entity_set(client, entity, id, value, changed_hooks)
@@ -242,13 +282,13 @@ function client_replicator.entity_set_pair(
242
282
  changed_hooks: ChangedHooksEntry?
243
283
  )
244
284
  if client.command_buffers_active then
245
- local command_buffer = get_command_buffer(client, entity)
246
- local pair_relation = command_buffer.pairs_values[relation]
285
+ local command_buffer = get_or_create_command_buffer(client, entity)
286
+ local pair_relation = command_buffer.pairs_set[relation]
247
287
 
248
288
  if pair_relation then
249
289
  pair_relation[target] = { value = value }
250
290
  else
251
- command_buffer.pairs_values[relation] = { [target] = { value = value } }
291
+ command_buffer.pairs_set[relation] = { [target] = { value = value } }
252
292
  end
253
293
  else
254
294
  client_replicator.entity_set(client, entity, PAIR(relation, target), value, changed_hooks)
@@ -263,22 +303,56 @@ function client_replicator.entity_add_pair(
263
303
  changed_hooks: ChangedHooksEntry?
264
304
  )
265
305
  if client.command_buffers_active then
266
- local command_buffer = get_command_buffer(client, entity)
267
- local pair_relation = command_buffer.pairs[relation]
306
+ local command_buffer = get_or_create_command_buffer(client, entity)
307
+ local pair_relation = command_buffer.pairs_add[relation]
268
308
 
269
309
  if pair_relation then
270
310
  pair_relation[target] = true
271
311
  else
272
- command_buffer.pairs[relation] = { [target] = true }
312
+ command_buffer.pairs_add[relation] = { [target] = true }
273
313
  end
274
314
  else
275
315
  client_replicator.entity_add(client, entity, PAIR(relation, target), changed_hooks)
276
316
  end
277
317
  end
278
318
 
319
+ function client_replicator.entity_set_postponed_pair(
320
+ client: Client,
321
+ entity: Entity,
322
+ relation: Component,
323
+ target: Entity,
324
+ c: Cursor,
325
+ variants: { any }?
326
+ ): any
327
+ if client.command_buffers_active then
328
+ local commmand_buffer = get_or_create_command_buffer(client, entity)
329
+ local pairs_unprocessed = commmand_buffer.pairs_unprocessed
330
+ local pair_relation = pairs_unprocessed[relation]
331
+ if pair_relation then
332
+ pair_relation[target] = {
333
+ cursor = c,
334
+ variants = variants,
335
+ }
336
+ else
337
+ pairs_unprocessed[relation] = {
338
+ [target] = {
339
+ cursor = c,
340
+ variants = variants,
341
+ },
342
+ }
343
+ end
344
+ return nil
345
+ else
346
+ local pair_id, value = client_replicator.read_pair_value(client, relation, target, c, variants)
347
+ -- TODO: respect hooks
348
+ WORLD_SET(client.world, entity, pair_id, value)
349
+ return value
350
+ end
351
+ end
352
+
279
353
  function client_replicator.entity_remove(client: Client, entity: Entity, id: Entity, removed_hooks: RemovedHooksEntry?)
280
354
  if client.command_buffers_active then
281
- local command_buffer = get_command_buffer(client, entity)
355
+ local command_buffer = get_or_create_command_buffer(client, entity)
282
356
  command_buffer.remove[id] = true
283
357
  else
284
358
  if removed_hooks then
@@ -286,10 +360,10 @@ function client_replicator.entity_remove(client: Client, entity: Entity, id: Ent
286
360
  callback(entity, id)
287
361
  end
288
362
  if not removed_hooks.overrides then
289
- client.world:remove(entity, id)
363
+ WORLD_REMOVE(client.world, entity, id)
290
364
  end
291
365
  else
292
- client.world:remove(entity, id)
366
+ WORLD_REMOVE(client.world, entity, id)
293
367
  end
294
368
  end
295
369
  end
@@ -302,7 +376,7 @@ function client_replicator.entity_remove_pair(
302
376
  removed_hooks: RemovedHooksEntry?
303
377
  )
304
378
  if client.command_buffers_active then
305
- local command_buffer = get_command_buffer(client, entity)
379
+ local command_buffer = get_or_create_command_buffer(client, entity)
306
380
  local pair_relation = command_buffer.pairs_remove[relation]
307
381
 
308
382
  if pair_relation then
@@ -315,97 +389,91 @@ function client_replicator.entity_remove_pair(
315
389
  end
316
390
  end
317
391
 
318
- function client_replicator.entity_remove_relations(
392
+ function client_replicator.entity_clear_relation(
319
393
  client: Client,
320
394
  entity: Entity,
321
395
  relation: Entity,
322
396
  removed_hooks: RemovedHooksEntry?
323
397
  )
324
398
  if client.command_buffers_active then
325
- local command_buffer = get_command_buffer(client, entity)
326
- command_buffer.pairs_clear[relation] = true
399
+ local command_buffer = get_or_create_command_buffer(client, entity)
400
+ command_buffer.relations_clear[relation] = true
327
401
  else
328
402
  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
334
-
335
- local wildcard = PAIR(relation, WILDCARD) :: any
336
- local idr = world.component_index[wildcard]
337
- if not idr then
338
- return
339
- end
340
-
341
- local archetype_id = archetype.id
342
- local count = idr.counts[archetype_id]
343
- if not count then
344
- return
345
- end
346
-
347
- local start = idr.records[archetype_id]
348
- if not start then
349
- return
350
- end
351
-
352
403
  if removed_hooks then
353
- for i = start, start + count - 1 do
354
- local pair = archetype.types[i]
404
+ ITERATE_ALL_RELATIONS(world, entity, relation, function(pair)
355
405
  for _, callback in removed_hooks.callbacks do
356
406
  callback(entity, pair)
357
407
  end
358
408
  if not removed_hooks.overrides then
359
- world:remove(entity, pair :: any)
409
+ WORLD_REMOVE(world, entity, pair)
360
410
  end
361
- end
411
+ end)
362
412
  else
363
- for i = start, start + count - 1 do
364
- local pair = archetype.types[i]
365
- world:remove(entity, pair :: any)
366
- end
413
+ ITERATE_ALL_RELATIONS(world, entity, relation, function(pair)
414
+ WORLD_REMOVE(world, entity, pair)
415
+ end)
367
416
  end
368
417
  end
369
418
  end
370
419
 
371
- function client_replicator.encode_component(client: Client, component: Entity): number
372
- local encoded = client.shared.components.members[component]
373
- if not encoded then
374
- utils.logerror(`attempted to encode a non-shared component `, utils.logcomponent(client.world, component))
375
- return 0
376
- end
377
- return encoded
378
- end
379
- function client_replicator.decode_component(client: Client, encoded: number): Entity
380
- local component = client.shared.components.indexes[encoded]
381
- if not component then
382
- print("NON SHARED COMPONENT", encoded, client.shared.components)
383
- error "attemped to decode a non shared component"
420
+ local function read_variant_value(c: Cursor, serdes: types.Serdes?, variants: { any }?)
421
+ if serdes then
422
+ local bytespan = serdes.bytespan or cursor.read_vlq(c)
423
+
424
+ local appended = cursor.read_buffer(c, bytespan)
425
+ local serdes_variants: { any }?
426
+
427
+ if serdes.includes_variants then
428
+ local start_variant = cursor.read_vlq(c)
429
+
430
+ if start_variant > 0 then
431
+ local size = cursor.read_vlq(c)
432
+ serdes_variants = table.move(variants :: { any }, start_variant, start_variant + size - 1, 1, {})
433
+ end
434
+ end
435
+
436
+ local output = serdes.deserialize(appended, serdes_variants :: any)
437
+ return output
438
+ else
439
+ local variant_id = cursor.read_vlq(c)
440
+ if variant_id == 0 then
441
+ return nil
442
+ end
443
+ local value = (variants :: { any })[variant_id]
444
+ return value
384
445
  end
385
- return component
386
446
  end
387
447
 
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]
392
- if not component then
393
- print("NON SHARED COMPONENT", shared_id, client.shared.components)
394
- error "received a non shared component"
395
- end
396
- return component
448
+ function client_replicator.read_pair_value(
449
+ client: Client,
450
+ relation: Component,
451
+ target: Entity,
452
+ c: Cursor,
453
+ variants: { any }?
454
+ )
455
+ local pair_id = PAIR(relation, target)
456
+ local pair_wildcard = PAIR(relation, ECS_WILDCARD)
457
+ local serdes = client.component_serdes[pair_id] or client.component_serdes[pair_wildcard]
458
+ local value = read_variant_value(c, serdes, variants)
459
+ return pair_id, value
397
460
  end
398
461
 
399
- function client_replicator.read_component_value(client: Client, component: Entity, c: Cursor, variants: { any }?): any
462
+ function client_replicator.read_component_value(
463
+ client: Client,
464
+ component: Component,
465
+ c: Cursor,
466
+ variants: { any }?
467
+ ): any
400
468
  local serdes = client.shared.serdes[component]
401
469
 
402
470
  if serdes then
403
471
  local bytespan = serdes.bytespan or cursor.read_vlq(c)
404
472
 
405
473
  local appended = cursor.read_buffer(c, bytespan)
406
- local serdes_variants: { any }? = nil
407
-
408
- if serdes.includes_variants then
474
+ local serdes_variants: { any }?
475
+
476
+ if serdes.includes_variants then
409
477
  local start_variant = cursor.read_vlq(c)
410
478
 
411
479
  if start_variant > 0 then
@@ -414,7 +482,7 @@ function client_replicator.read_component_value(client: Client, component: Entit
414
482
  end
415
483
  end
416
484
 
417
- local output = serdes.deserialize(appended, serdes_variants)
485
+ local output = serdes.deserialize(appended, serdes_variants :: any)
418
486
  return output
419
487
  else
420
488
  local variant_id = cursor.read_vlq(c)
@@ -426,10 +494,15 @@ function client_replicator.read_component_value(client: Client, component: Entit
426
494
  end
427
495
  end
428
496
 
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)
432
- return component, value
497
+ function client_replicator.read_component_id(client: Client, c: Cursor): Entity
498
+ local shared_components = client.shared.components
499
+ local shared_id = cursor.read_span(c, #shared_components.keys)
500
+ local component = shared_components.indexes[shared_id]
501
+ if not component then
502
+ print("NON SHARED COMPONENT", shared_id, client.shared.components)
503
+ error "received a non shared component"
504
+ end
505
+ return component
433
506
  end
434
507
 
435
508
  function client_replicator.resolve_global(client: Client, global_id: number): Entity
@@ -461,6 +534,14 @@ function client_replicator.read_entity_id(client: Client, c: Cursor): (Entity?,
461
534
  end
462
535
  end
463
536
 
537
+ local function read_vlq_bitmask(c: Cursor, mask: number, bit: number): number
538
+ if utils.checkbit(mask, bit) then
539
+ return cursor.read_vlq(c)
540
+ else
541
+ return 0
542
+ end
543
+ end
544
+
464
545
  function client_replicator.process_entity_id(
465
546
  client: Client,
466
547
  c: Cursor,
@@ -488,7 +569,7 @@ function client_replicator.process_entity_id(
488
569
  local handler = client.shared.custom_ids.indexes[shared_custom_id]
489
570
 
490
571
  if not handler then
491
- utils.logerror "attempted to use a custom id that wasn't registered"
572
+ common.log_error "attempted to use a custom id that wasn't registered"
492
573
  end
493
574
  custom_ids[server_id :: any] = handler
494
575
  end
@@ -509,16 +590,11 @@ function client_replicator.process_entity_id(
509
590
  end
510
591
  end
511
592
 
512
- function client_replicator.process_entity_relations(
513
- client: Client,
514
- entity: jecs.Entity,
515
- c: Cursor,
516
- custom_ids: CustomIdMap
517
- )
593
+ function client_replicator.process_entity_relations(client: Client, entity: Entity, c: Cursor, custom_ids: CustomIdMap)
518
594
  local components = client.components
519
595
  local relation = client_replicator.read_component_id(client, c)
520
596
  local total_targets = cursor.read_vlq(c)
521
- local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
597
+ local pair_hooks = client.network_hooks.changed[PAIR(components.relation, relation)]
522
598
 
523
599
  for _ = 1, total_targets do
524
600
  client_replicator.process_entity_id(client, c, custom_ids, function(target)
@@ -529,7 +605,7 @@ end
529
605
 
530
606
  function client_replicator.process_entity_relation_values(
531
607
  client: Client,
532
- entity: jecs.Entity,
608
+ entity: Entity,
533
609
  c: Cursor,
534
610
  custom_ids: CustomIdMap,
535
611
  variants: { any }?
@@ -537,47 +613,99 @@ function client_replicator.process_entity_relation_values(
537
613
  local relation = client_replicator.read_component_id(client, c)
538
614
  local total_targets = cursor.read_vlq(c)
539
615
  local components = client.components
540
- local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
616
+ local relation_hooks = client.network_hooks.changed[PAIR(components.relation, relation)]
541
617
 
542
618
  for _ = 1, total_targets do
543
619
  local value = client_replicator.read_component_value(client, relation, c, variants)
544
620
  client_replicator.process_entity_id(client, c, custom_ids, function(target)
545
- client_replicator.entity_set_pair(client, entity, relation, target, value, pair_hooks)
621
+ client_replicator.entity_set_pair(client, entity, relation, target, value, relation_hooks)
546
622
  end)
547
623
  end
548
624
  end
549
625
 
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
626
+ function client_replicator.process_entity_pair_value(
627
+ client: Client,
628
+ entity: Entity,
629
+ c: Cursor,
630
+ custom_ids: CustomIdMap,
631
+ variants: { any }?
632
+ )
633
+ local relation = client_replicator.read_component_id(client, c)
634
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
635
+ if (target :: any) > 0 then
636
+ local pointer_offset = cursor.read_vlq(c)
637
+ local jump_offset = c.offset - pointer_offset
554
638
 
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
639
+ local _, value = client_replicator.read_pair_value(client, relation, target, c, variants)
640
+ client_replicator.entity_set_pair(client, entity, relation, target, value)
561
641
 
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
642
+ c.offset = jump_offset
643
+ else
644
+ local frozen_cursor = cursor.from_offset(c.buffer, c.offset)
568
645
 
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
646
+ local pointer_offset = cursor.read_vlq(c)
647
+ c.offset -= pointer_offset
573
648
 
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)
649
+ client_replicator.entity_set_postponed_pair(client, entity, relation, target, frozen_cursor, variants)
577
650
  end
578
651
  end)
579
652
  end
580
653
 
654
+ function client_replicator.process_entity_pair(client: Client, entity: Entity, c: Cursor, custom_ids: CustomIdMap)
655
+ local relation = client_replicator.read_component_id(client, c)
656
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
657
+ -- TODO: respect hooks/overrides
658
+ client_replicator.entity_add_pair(client, entity, relation, target)
659
+ end)
660
+ end
661
+
662
+ function client_replicator.process_entity(
663
+ client: Client,
664
+ entity: Entity,
665
+ c: Cursor,
666
+ custom_ids: CustomIdMap,
667
+ variants: { any }?
668
+ )
669
+ local mask = cursor.readu8(c)
670
+ local changed_hooks = client.network_hooks.changed
671
+ local components = client.components
672
+
673
+ local total_tags = read_vlq_bitmask(c, mask, 0)
674
+ for _ = 1, total_tags do
675
+ local tag = client_replicator.read_component_id(client, c)
676
+ local tag_hooks = changed_hooks[PAIR(components.reliable, tag)]
677
+ client_replicator.entity_add(client, entity, tag, tag_hooks)
678
+ end
679
+
680
+ local total_components = read_vlq_bitmask(c, mask, 1)
681
+ for _ = 1, total_components do
682
+ local component = client_replicator.read_component_id(client, c)
683
+ local value = client_replicator.read_component_value(client, component, c, variants)
684
+ local component_hooks = changed_hooks[PAIR(components.reliable, component)]
685
+ client_replicator.entity_set(client, entity, component, value, component_hooks)
686
+ end
687
+
688
+ local total_pairs = read_vlq_bitmask(c, mask, 2)
689
+ for _ = 1, total_pairs do
690
+ client_replicator.process_entity_pair(client, entity, c, custom_ids)
691
+ end
692
+
693
+ local total_pair_values = read_vlq_bitmask(c, mask, 3)
694
+ for _ = 1, total_pair_values do
695
+ client_replicator.process_entity_pair_value(client, entity, c, custom_ids, variants)
696
+ end
697
+
698
+ local total_relations = read_vlq_bitmask(c, mask, 4)
699
+ for _ = 1, total_relations do
700
+ client_replicator.process_entity_relations(client, entity, c, custom_ids)
701
+ end
702
+
703
+ local total_relation_values = read_vlq_bitmask(c, mask, 5)
704
+ for _ = 1, total_relation_values do
705
+ client_replicator.process_entity_relation_values(client, entity, c, custom_ids, variants)
706
+ end
707
+ end
708
+
581
709
  function client_replicator.finish_replication(client: Client)
582
710
  client.is_replicating = false
583
711
  for _, callback in client.after_replication_callbacks do
@@ -588,32 +716,33 @@ end
588
716
 
589
717
  function client_replicator.resolve_dirty(client: Client)
590
718
  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
719
+ -- stylua: ignore
720
+ client.shared = utils.resolved_shared(
721
+ client.world,
722
+ client.components,
723
+ client.registered_custom_ids,
724
+ client.component_serdes
725
+ )
726
+ client.is_shared_dirty = false
593
727
  end
594
728
  end
595
729
 
596
- local function check_packet_type(c: Cursor, type: string)
597
- local byte_type = PACKET_TYPES[type]
598
- local packet_type = cursor.readu8(c)
599
- if packet_type ~= byte_type then
600
- local got: string = nil :: any
601
- for k, v in PACKET_TYPES do
602
- if v == packet_type then
603
- got = k :: string
604
- break
730
+ local function check_packet_type(c: Cursor, expected_byte: number)
731
+ local got_byte = cursor.readu8(c)
732
+ if got_byte ~= expected_byte then
733
+ local got_type: string?
734
+ local expected_type: string?
735
+
736
+ for packet_type, byte in PACKET_TYPES :: { [string]: number } do
737
+ if byte == got_byte then
738
+ got_type = packet_type
739
+ elseif byte == expected_byte then
740
+ expected_type = packet_type
605
741
  end
606
742
  end
607
-
608
- error(`packet type mismatch, expected {type} got {got} instead`)
609
- end
610
- end
611
-
612
- local function read_vlq_bitmask(c: Cursor, mask: number, bit: number): number
613
- if utils.checkbit(mask, bit) then
614
- return cursor.read_vlq(c)
615
- else
616
- return 0
743
+ common.log_error(
744
+ `packet type mismatch, expected: {expected_type or "(unknown)"} got: {got_type or "(unknown)"} instead`
745
+ )
617
746
  end
618
747
  end
619
748
 
@@ -621,7 +750,7 @@ function client_replicator.apply_command_buffer(
621
750
  client: Client,
622
751
  entity: Entity,
623
752
  command_buffer: CommandBuffer,
624
- request: (e: Entity, ...any) -> Entity?
753
+ request: (e: Entity,...any) -> Entity?
625
754
  )
626
755
  local components = client.components
627
756
 
@@ -641,36 +770,59 @@ function client_replicator.apply_command_buffer(
641
770
  local component_hooks = client.network_hooks.removed[PAIR(components.reliable, component)]
642
771
  client_replicator.entity_remove(client, entity, component, component_hooks)
643
772
  end
644
- for relation, targets in command_buffer.pairs do
645
- local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
773
+ for relation, targets in command_buffer.pairs_add do
774
+ local relation_hooks = client.network_hooks.changed[PAIR(components.relation, relation)]
646
775
  for target in targets do
647
776
  local client_target = request(target)
648
777
  if client_target then
649
- client_replicator.entity_add_pair(client, entity, relation, client_target, pair_hooks)
778
+ client_replicator.entity_add_pair(client, entity, relation, client_target, relation_hooks)
650
779
  end
651
780
  end
652
781
  end
653
- for relation, targets in command_buffer.pairs_values do
654
- local pair_hooks = client.network_hooks.changed[PAIR(components.pair, relation)]
782
+ for relation, targets in command_buffer.pairs_set do
783
+ local relation_hooks = client.network_hooks.changed[PAIR(components.relation, relation)]
655
784
  for target, value in targets do
656
785
  local client_target = request(target)
657
786
  if client_target then
658
- client_replicator.entity_set_pair(client, entity, relation, client_target, value.value, pair_hooks)
787
+ client_replicator.entity_set_pair(client, entity, relation, client_target, value.value, relation_hooks)
659
788
  end
660
789
  end
661
790
  end
791
+ for relation, targets in command_buffer.pairs_unprocessed do
792
+ -- TODO: respect hooks
793
+ for target, unprocessed in targets do
794
+ local client_target = request(target)
795
+ if client_target then
796
+ local processed_value = unprocessed.processed_value
797
+ if processed_value then
798
+ client_replicator.entity_set_pair(client, entity, relation, client_target, processed_value.value)
799
+ else
800
+ local value = client_replicator.entity_set_postponed_pair(
801
+ client,
802
+ entity,
803
+ relation,
804
+ client_target,
805
+ unprocessed.cursor,
806
+ unprocessed.variants
807
+ )
808
+ unprocessed.processed_value = { value = value }
809
+ end
810
+ end
811
+ end
812
+ end
813
+
662
814
  for relation, targets in command_buffer.pairs_remove do
663
- local pair_hooks = client.network_hooks.removed[PAIR(components.pair, relation)]
815
+ local relation_hooks = client.network_hooks.removed[PAIR(components.relation, relation)]
664
816
  for target in targets do
665
817
  local client_target = request(target)
666
818
  if client_target then
667
- client_replicator.entity_remove_pair(client, entity, relation, client_target, pair_hooks)
819
+ client_replicator.entity_remove_pair(client, entity, relation, client_target, relation_hooks)
668
820
  end
669
821
  end
670
822
  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)
823
+ for relation in command_buffer.relations_clear do
824
+ local relation_hooks = client.network_hooks.removed[PAIR(components.relation, relation)]
825
+ client_replicator.entity_clear_relation(client, entity, relation, relation_hooks)
674
826
  end
675
827
  end
676
828
 
@@ -683,32 +835,44 @@ function client_replicator.find_has_in_buffer(command_buffer: CommandBuffer, tag
683
835
  or (command_buffer.components[tag] ~= nil)
684
836
  or (command_buffer.unreliable_components[tag] ~= nil)
685
837
  end
838
+
686
839
  function client_replicator.find_pair_in_buffer(
687
840
  command_buffer: CommandBuffer,
688
841
  relation: Entity,
689
842
  target: Entity,
690
843
  find: (unprocessed_entity: Entity) -> (Entity?, Entity)
691
844
  ): (boolean, any)
692
- local value_relations = command_buffer.pairs_values[relation]
693
-
845
+ local value_relations = command_buffer.pairs_set[relation]
694
846
  if value_relations then
695
- for unprocessed_target, value in value_relations do
696
- local found = find(unprocessed_target)
847
+ for buffer_target, value in value_relations do
848
+ local found = find(buffer_target)
697
849
  if found == target then
698
850
  return true, value.value
699
851
  end
700
852
  end
701
853
  end
702
854
 
703
- local relations = command_buffer.pairs[relation]
855
+ local relations = command_buffer.pairs_add[relation]
704
856
  if relations then
705
- for unprocessed_target in relations do
706
- local found = find(unprocessed_target)
857
+ for buffer_target in relations do
858
+ local found = find(buffer_target)
707
859
  if found == target then
708
860
  return true, nil
709
861
  end
710
862
  end
711
863
  end
864
+
865
+ local unprocessed_relations = command_buffer.pairs_unprocessed[relation]
866
+ if unprocessed_relations then
867
+ for buffer_target, unprocessed in unprocessed_relations do
868
+ local found = find(buffer_target)
869
+ if found == target then
870
+ local processed_value = unprocessed.processed_value
871
+ return true, processed_value and processed_value.value
872
+ end
873
+ end
874
+ end
875
+
712
876
  return false, nil
713
877
  end
714
878
 
@@ -719,7 +883,7 @@ function client_replicator.find_target_in_buffer(
719
883
  ): Entity?
720
884
  local i = 0
721
885
 
722
- local pairs = command_buffer.pairs[relation]
886
+ local pairs = command_buffer.pairs_add[relation]
723
887
  if pairs then
724
888
  for target in pairs do
725
889
  if i == (index or 0) then
@@ -728,7 +892,8 @@ function client_replicator.find_target_in_buffer(
728
892
  i += 1
729
893
  end
730
894
  end
731
- local pairs_values = command_buffer.pairs_values[relation]
895
+
896
+ local pairs_values = command_buffer.pairs_set[relation]
732
897
  if pairs_values then
733
898
  for target in pairs_values do
734
899
  if i == (index or 0) then
@@ -737,6 +902,17 @@ function client_replicator.find_target_in_buffer(
737
902
  i += 1
738
903
  end
739
904
  end
905
+
906
+ local unprocessed_pairs = command_buffer.pairs_unprocessed[relation]
907
+ if unprocessed_pairs then
908
+ for target in unprocessed_pairs do
909
+ if i == (index or 0) then
910
+ return target
911
+ end
912
+ i += 1
913
+ end
914
+ end
915
+
740
916
  return nil
741
917
  end
742
918
 
@@ -745,6 +921,7 @@ function client_replicator.finish_custom_ids(
745
921
  custom_ids: CustomIdMap,
746
922
  command_buffers: { [Entity]: CommandBuffer }
747
923
  )
924
+ local world = client.world
748
925
  local funcs = {} -- this is so process can use create_context even if its defined after
749
926
  local processed: { [Entity]: boolean } = {}
750
927
 
@@ -771,18 +948,18 @@ function client_replicator.finish_custom_ids(
771
948
  return nil
772
949
  end
773
950
 
774
- local new_entity: Entity? = nil
775
- local command_buffer = command_buffers[unprocessed_entity :: any]
951
+ local new_entity: Entity?
952
+ local command_buffer = command_buffers[unprocessed_entity :: any]
776
953
  custom_handler = custom_handler or custom_ids[server_entity]
777
954
 
778
955
  if type(custom_handler) == "number" then
779
956
  local component: Entity = custom_handler :: any
780
957
 
781
- local handler = client.world:get(component, client.components.custom_handler)
958
+ local handler = WORLD_GET(world, component, client.components.custom_handler)
782
959
  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,
960
+ return common.log_error(
961
+ `received a custom id for a non custom component, consider adding custom_handler to component: {common.log_component(
962
+ world,
786
963
  component
787
964
  )}`
788
965
  )
@@ -792,19 +969,17 @@ function client_replicator.finish_custom_ids(
792
969
  if value then
793
970
  new_entity = handler(value.value)
794
971
  else
795
- utils.logwarn(
796
- `No component found for custom id handler: {utils.logcomponent(client.world, component)}`
797
- )
972
+ common.log_warn(`No component found for custom id handler: {common.log_component(world, component)}`)
798
973
  new_entity = handler(nil)
799
974
  end
800
975
  else
801
- utils.logwarn(`No component found for custom id handler: {utils.logcomponent(client.world, component)}`)
976
+ common.log_warn(`No component found for custom id handler: {common.log_component(world, component)}`)
802
977
  new_entity = handler(nil)
803
978
  end
804
979
  else
805
980
  local handle_callback = (custom_handler :: CustomId).handle_callback
806
981
  if not handle_callback then
807
- return utils.logerror(
982
+ return common.log_error(
808
983
  "no handler callback was set for custom id:",
809
984
  (custom_handler :: CustomId).identifier
810
985
  )
@@ -813,7 +988,11 @@ function client_replicator.finish_custom_ids(
813
988
  end
814
989
 
815
990
  if new_entity then
991
+ WORLD_ADD(world, new_entity, client.components.__alive_tracking__)
992
+
816
993
  client.server_ids[server_entity :: any] = new_entity
994
+ client.client_ids[new_entity] = server_entity :: any
995
+
817
996
  if command_buffer then
818
997
  client_replicator.apply_command_buffer(client, new_entity, command_buffer, process)
819
998
  command_buffers[unprocessed_entity] = nil
@@ -894,12 +1073,71 @@ function client_replicator.finish_custom_ids(
894
1073
  end
895
1074
  end
896
1075
 
897
- function client_replicator.apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
1076
+ function client_replicator.apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
1077
+ client_replicator.resolve_dirty(client)
1078
+ client.is_replicating = true
1079
+
898
1080
  local c = cursor.from(buf)
899
- check_packet_type(c, "reliable")
1081
+ check_packet_type(c, PACKET_TYPES.full)
1082
+
1083
+ local total_packets = cursor.read_vlq(c)
1084
+ local variant_start = (all_variants and #all_variants + 1) :: number
1085
+
1086
+ local command_buffers: { [Entity]: CommandBuffer } = {}
1087
+ local custom_ids: CustomIdMap = {}
1088
+
1089
+ client.command_buffers = command_buffers
1090
+
1091
+ for a = 1, total_packets do
1092
+ local total_entities = cursor.read_vlq(c)
1093
+ local variants = all_variants and all_variants[variant_start - a]
1094
+
1095
+ for _ = 1, total_entities do
1096
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
1097
+ client_replicator.process_entity(client, entity, c, custom_ids, variants)
1098
+ end)
1099
+ end
1100
+ end
1101
+
1102
+ client_replicator.finish_custom_ids(client, custom_ids, command_buffers)
1103
+ client_replicator.finish_replication(client)
1104
+ end
1105
+
1106
+ function client_replicator.apply_entity(client: Client, buf: buffer, all_variants: { { any } })
1107
+ client_replicator.resolve_dirty(client)
1108
+ client.is_replicating = true
1109
+
1110
+ local c = cursor.from(buf)
1111
+ check_packet_type(c, PACKET_TYPES.entity)
1112
+
1113
+ local total_packets = cursor.read_vlq(c)
1114
+ local variant_start = (all_variants and #all_variants + 1) :: number
1115
+
1116
+ local command_buffers: { [Entity]: CommandBuffer } = {}
1117
+ local custom_ids: CustomIdMap = {}
1118
+
1119
+ client.command_buffers = command_buffers
1120
+
1121
+ for a = 1, total_packets do
1122
+ local variants = all_variants and all_variants[variant_start - a]
1123
+
1124
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
1125
+ client_replicator.process_entity(client, entity, c, custom_ids, variants)
1126
+ end)
1127
+ end
1128
+
1129
+ client_replicator.finish_custom_ids(client, custom_ids, command_buffers)
1130
+ client_replicator.finish_replication(client)
1131
+ end
1132
+
1133
+ function client_replicator.apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
900
1134
  client_replicator.resolve_dirty(client)
901
1135
  client.is_replicating = true
902
1136
 
1137
+ local c = cursor.from(buf)
1138
+ check_packet_type(c, PACKET_TYPES.updates)
1139
+
1140
+ local world = client.world
903
1141
  local components = client.components
904
1142
  local total_packets = cursor.read_vlq(c)
905
1143
  local variant_start = (all_variants and #all_variants + 1) :: number
@@ -918,7 +1156,9 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
918
1156
 
919
1157
  local total_added = read_vlq_bitmask(c, storage_mask, 0)
920
1158
  for _ = 1, total_added do
921
- client_replicator.process_entity(client, c, custom_ids, variants)
1159
+ client_replicator.process_entity_id(client, c, custom_ids, function(entity)
1160
+ client_replicator.process_entity(client, entity, c, custom_ids, variants)
1161
+ end)
922
1162
  end
923
1163
 
924
1164
  local total_components_added = read_vlq_bitmask(c, storage_mask, 1)
@@ -935,18 +1175,29 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
935
1175
 
936
1176
  local total_components = read_vlq_bitmask(c, added_mask, 1)
937
1177
  for _ = 1, total_components do
938
- local component, value = client_replicator.read_component(client, c, variants)
1178
+ local component = client_replicator.read_component_id(client, c)
1179
+ local value = client_replicator.read_component_value(client, component, c, variants)
939
1180
  local component_hooks = changed_hooks[PAIR(components.reliable, component)]
940
1181
  client_replicator.entity_set(client, entity, component, value, component_hooks)
941
1182
  end
942
1183
 
943
1184
  local total_pairs = read_vlq_bitmask(c, added_mask, 2)
944
1185
  for _ = 1, total_pairs do
1186
+ client_replicator.process_entity_pair(client, entity, c, custom_ids)
1187
+ end
1188
+
1189
+ local total_pair_values = read_vlq_bitmask(c, added_mask, 3)
1190
+ for _ = 1, total_pair_values do
1191
+ client_replicator.process_entity_pair_value(client, entity, c, custom_ids, variants)
1192
+ end
1193
+
1194
+ local total_relations = read_vlq_bitmask(c, added_mask, 4)
1195
+ for _ = 1, total_relations do
945
1196
  client_replicator.process_entity_relations(client, entity, c, custom_ids)
946
1197
  end
947
1198
 
948
- local total_pairs_values = read_vlq_bitmask(c, added_mask, 3)
949
- for _ = 1, total_pairs_values do
1199
+ local total_relation_values = read_vlq_bitmask(c, added_mask, 5)
1200
+ for _ = 1, total_relation_values do
950
1201
  client_replicator.process_entity_relation_values(client, entity, c, custom_ids, variants)
951
1202
  end
952
1203
  end)
@@ -966,7 +1217,8 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
966
1217
 
967
1218
  local total_components = read_vlq_bitmask(c, changed_mask, 1)
968
1219
  for _ = 1, total_components do
969
- local component, value = client_replicator.read_component(client, c, variants)
1220
+ local component = client_replicator.read_component_id(client, c)
1221
+ local value = client_replicator.read_component_value(client, component, c, variants)
970
1222
  local component_hooks = changed_hooks[PAIR(components.reliable, component)]
971
1223
  client_replicator.entity_set(client, entity, component, value, component_hooks)
972
1224
  end
@@ -978,57 +1230,63 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
978
1230
  client_replicator.entity_remove(client, entity, component, component_hooks)
979
1231
  end
980
1232
 
981
- local total_pairs = read_vlq_bitmask(c, changed_mask, 3)
982
- for _ = 1, total_pairs do
1233
+ local total_pairs_added = read_vlq_bitmask(c, changed_mask, 3)
1234
+ for _ = 1, total_pairs_added do
1235
+ client_replicator.process_entity_pair(client, entity, c, custom_ids)
1236
+ end
1237
+
1238
+ local total_pairs_set = read_vlq_bitmask(c, changed_mask, 4)
1239
+ for _ = 1, total_pairs_set do
1240
+ client_replicator.process_entity_pair(client, entity, c, custom_ids)
1241
+ end
1242
+
1243
+ local total_pairs_removed = read_vlq_bitmask(c, changed_mask, 5)
1244
+ for _ = 1, total_pairs_removed do
1245
+ local relation = client_replicator.read_component_id(client, c)
1246
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
1247
+ -- TODO: respect hooks/overrides
1248
+ client_replicator.entity_remove_pair(client, entity, relation, target)
1249
+ end)
1250
+ end
1251
+
1252
+ local total_relations_added = read_vlq_bitmask(c, changed_mask, 6)
1253
+ for _ = 1, total_relations_added do
983
1254
  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
1255
  local total_targets = cursor.read_vlq(c)
1256
+ local pair_changed_hooks = changed_hooks[PAIR(components.relation, relation)]
987
1257
 
988
1258
  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)
1005
- end
1259
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
1260
+ client_replicator.entity_add_pair(client, entity, relation, target, pair_changed_hooks)
1261
+ end)
1006
1262
  end
1007
1263
  end
1008
1264
 
1009
- local total_pairs_values_removed = read_vlq_bitmask(c, changed_mask, 4)
1010
- for _ = 1, total_pairs_values_removed do
1265
+ local total_relation_values = read_vlq_bitmask(c, changed_mask, 7)
1266
+ for _ = 1, total_relation_values do
1011
1267
  local relation = client_replicator.read_component_id(client, c)
1012
- local pair_hooks = removed_hooks[PAIR(components.pair, relation)]
1013
1268
  local total_targets = cursor.read_vlq(c)
1269
+ local pair_changed_hooks = changed_hooks[PAIR(components.relation, relation)]
1014
1270
 
1015
1271
  for _ = 1, total_targets do
1272
+ local value = client_replicator.read_component_value(client, relation, c, variants)
1016
1273
  client_replicator.process_entity_id(client, c, custom_ids, function(target)
1017
- client_replicator.entity_remove_pair(client, entity, relation, target, pair_hooks)
1274
+ client_replicator.entity_set_pair(client, entity, relation, target, value, pair_changed_hooks)
1018
1275
  end)
1019
1276
  end
1020
1277
  end
1021
1278
 
1022
- local total_pairs_values = read_vlq_bitmask(c, changed_mask, 5)
1023
- for _ = 1, total_pairs_values do
1279
+ -- using a normal vlq here sadly
1280
+ -- cause I ran out of bitmask space for this one :/
1281
+ local total_relations_removed = cursor.read_vlq(c)
1282
+ for _ = 1, total_relations_removed do
1024
1283
  local relation = client_replicator.read_component_id(client, c)
1025
- local pair_hooks = changed_hooks[PAIR(components.pair, relation)]
1026
1284
  local total_targets = cursor.read_vlq(c)
1285
+ local pair_removed_hooks = removed_hooks[PAIR(components.relation, relation)]
1027
1286
 
1028
1287
  for _ = 1, total_targets do
1029
- local value = client_replicator.read_component_value(client, relation, c, variants)
1030
1288
  client_replicator.process_entity_id(client, c, custom_ids, function(target)
1031
- client_replicator.entity_set_pair(client, entity, relation, target, value, pair_hooks)
1289
+ client_replicator.entity_remove_pair(client, entity, relation, target, pair_removed_hooks)
1032
1290
  end)
1033
1291
  end
1034
1292
  end
@@ -1038,8 +1296,8 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
1038
1296
  local total_component_deletions = read_vlq_bitmask(c, storage_mask, 3)
1039
1297
  for _ = 1, total_component_deletions do
1040
1298
  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
1299
+ local total_components = cursor.read_vlq(c)
1300
+ for _ = 1, total_components do
1043
1301
  local component = client_replicator.read_component_id(client, c)
1044
1302
  local component_hooks = removed_hooks[PAIR(components.reliable, component)]
1045
1303
  client_replicator.entity_remove(client, entity, component, component_hooks)
@@ -1048,8 +1306,17 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
1048
1306
  local total_pairs = cursor.read_vlq(c)
1049
1307
  for _ = 1, total_pairs do
1050
1308
  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)
1309
+ client_replicator.process_entity_id(client, c, custom_ids, function(target)
1310
+ -- TODO: respect hooks/overrides
1311
+ client_replicator.entity_remove_pair(client, entity, relation, target)
1312
+ end)
1313
+ end
1314
+
1315
+ local total_relations = cursor.read_vlq(c)
1316
+ for _ = 1, total_relations do
1317
+ local relation = client_replicator.read_component_id(client, c)
1318
+ local pair_hooks = removed_hooks[PAIR(components.relation, relation)]
1319
+ client_replicator.entity_clear_relation(client, entity, relation, pair_hooks)
1053
1320
  end
1054
1321
  end)
1055
1322
  end
@@ -1064,10 +1331,10 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
1064
1331
  callback(entity)
1065
1332
  end
1066
1333
  if not deleted_hook.overrides then
1067
- client.world:delete(entity)
1334
+ WORLD_DELETE(world, entity)
1068
1335
  end
1069
1336
  else
1070
- client.world:delete(entity)
1337
+ WORLD_DELETE(world, entity)
1071
1338
  end
1072
1339
  end
1073
1340
  if server_id then
@@ -1081,11 +1348,12 @@ function client_replicator.apply_updates(client: Client, buf: buffer, all_varian
1081
1348
  end
1082
1349
 
1083
1350
  function client_replicator.apply_unreliable(client: Client, buf: buffer, all_variants: { { any } }?)
1084
- local c = cursor.from(buf)
1085
- check_packet_type(c, "unreliable")
1086
1351
  client_replicator.resolve_dirty(client)
1087
1352
  client.is_replicating = true
1088
1353
 
1354
+ local c = cursor.from(buf)
1355
+ check_packet_type(c, PACKET_TYPES.unreliable)
1356
+
1089
1357
  local components = client.components
1090
1358
  local total_packets = cursor.read_vlq(c)
1091
1359
  local variant_start = (all_variants and #all_variants + 1) :: number
@@ -1101,15 +1369,17 @@ function client_replicator.apply_unreliable(client: Client, buf: buffer, all_var
1101
1369
 
1102
1370
  if entity then
1103
1371
  for _ = 1, total_unreliable do
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)
1372
+ local component = client_replicator.read_component_id(client, c)
1373
+ local value = client_replicator.read_component_value(client, component, c, variants)
1374
+ local unreliable_hooks = changed_hooks[PAIR(components.unreliable, component)]
1375
+ client_replicator.entity_set_unreliable(client, entity, component, value, unreliable_hooks)
1107
1376
  end
1108
1377
  else
1109
1378
  -- dont apply unreliable updates to entities that dont exist in the client yet
1110
1379
  -- but we still need to process the values to keep the cursor in the right place
1111
1380
  for _ = 1, total_unreliable do
1112
- client_replicator.read_component(client, c, variants)
1381
+ local component = client_replicator.read_component_id(client, c)
1382
+ client_replicator.read_component_value(client, component, c, variants)
1113
1383
  end
1114
1384
  end
1115
1385
  end
@@ -1118,34 +1388,19 @@ function client_replicator.apply_unreliable(client: Client, buf: buffer, all_var
1118
1388
  client_replicator.finish_replication(client)
1119
1389
  end
1120
1390
 
1121
- function client_replicator.apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
1122
- local c = cursor.from(buf)
1123
- check_packet_type(c, "full")
1124
- client_replicator.resolve_dirty(client)
1125
- client.is_replicating = true
1126
-
1127
- local total_packets = cursor.read_vlq(c)
1128
- local variant_start = (all_variants and #all_variants + 1) :: number
1129
-
1130
- local command_buffers: { [Entity]: CommandBuffer } = {}
1131
- local custom_ids: CustomIdMap = {}
1132
-
1133
- client.command_buffers = command_buffers
1134
-
1135
- for a = 1, total_packets do
1136
- local total_entities = cursor.read_vlq(c)
1137
- local variants = all_variants and all_variants[variant_start - a]
1138
-
1139
- for _ = 1, total_entities do
1140
- client_replicator.process_entity(client, c, custom_ids, variants)
1141
- end
1142
- end
1391
+ function client_replicator.set_serdes(client: Client, component: Component, serdes: types.Serdes)
1392
+ client.component_serdes[component] = serdes
1393
+ client.is_shared_dirty = true
1394
+ end
1143
1395
 
1144
- client_replicator.finish_custom_ids(client, custom_ids, command_buffers)
1145
- client_replicator.finish_replication(client)
1396
+ function client_replicator.remove_serdes(client: Client, component: Component)
1397
+ client.component_serdes[component] = nil
1398
+ client.is_shared_dirty = true
1146
1399
  end
1147
1400
 
1148
- function client_replicator.init(client: Client, _world: World?)
1401
+ -- TODO: Deserialize pairs in client
1402
+
1403
+ function client_replicator.init(client: Client, wrd: World?)
1149
1404
  if client.inited == true then
1150
1405
  return warn "attempted to init a client twice"
1151
1406
  end
@@ -1154,44 +1409,57 @@ function client_replicator.init(client: Client, _world: World?)
1154
1409
  end
1155
1410
  client.inited = true :: any
1156
1411
 
1157
- local world = _world or client.world
1412
+ local world = wrd or client.world
1158
1413
  client.world = world
1159
1414
 
1160
1415
  if not world then
1161
1416
  error "Providing a world is required to start replecs"
1162
1417
  end
1163
1418
 
1164
- local hooks = {
1165
- added = (world :: any).added,
1166
- changed = (world :: any).changed,
1167
- removed = (world :: any).removed,
1168
- }
1169
- client.hooks = hooks
1419
+ if not client.components then
1420
+ client.components = utils.create_components(utils.tag_factory(world), utils.component_factory(world))
1421
+ utils.add_component_names(client.components, function(component, name)
1422
+ common.add_name(world, component, name)
1423
+ end)
1424
+ end
1170
1425
 
1171
1426
  local components = client.components
1172
1427
 
1173
- client.shared = utils.resolved_shared(world, components, client.registered_custom_ids)
1428
+ local function hook(unhook: () -> ())
1429
+ table.insert(client.hooked, unhook)
1430
+ end
1174
1431
 
1175
- local alive_unhook = hooks.removed(world, components.__alive_tracking__, function(entity)
1432
+ hook(HOOK_REMOVED(world, components.__alive_tracking__, function(entity)
1176
1433
  local server_id = client.client_ids[entity]
1177
1434
  if server_id then
1178
1435
  client.server_ids[server_id] = nil
1179
1436
  client.client_ids[entity] = nil
1180
1437
  end
1181
1438
  client.network_hooks.deleted[entity] = nil
1182
- end)
1183
- table.insert(client.hooked, alive_unhook)
1439
+ end))
1440
+
1441
+ hook(HOOK_ADDED(world, components.serdes, function(component, _, serdes)
1442
+ client_replicator.set_serdes(client, component, serdes)
1443
+ end))
1444
+ hook(HOOK_CHANGED(world, components.serdes, function(component, _, serdes)
1445
+ client_replicator.set_serdes(client, component, serdes)
1446
+ end))
1447
+ hook(HOOK_REMOVED(world, components.serdes, function(component)
1448
+ client_replicator.remove_serdes(client, component)
1449
+ end))
1184
1450
 
1185
1451
  for _, component in client.requires_shared_lookup do
1186
- local added_hook = hooks.added(world, component, function()
1452
+ hook(HOOK_ADDED(world, component, function()
1187
1453
  client.is_shared_dirty = true
1188
- end)
1189
- local removed_hook = hooks.removed(world, component, function()
1454
+ end))
1455
+ hook(HOOK_REMOVED(world, component, function()
1190
1456
  client.is_shared_dirty = true
1191
- end)
1192
- table.insert(client.hooked, added_hook)
1193
- table.insert(client.hooked, removed_hook)
1457
+ end))
1194
1458
  end
1459
+
1460
+ QUERY_CURRENT_COMPONENTS(client)
1461
+
1462
+ client.shared = utils.resolved_shared(world, components, client.registered_custom_ids, client.component_serdes)
1195
1463
  end
1196
1464
 
1197
1465
  function client_replicator.after_replication(client: Client, callback: () -> ())
@@ -1215,7 +1483,7 @@ end
1215
1483
  local function add_hook_entry(client: Client, action: string, ...): ({ overrides: boolean }, () -> ())
1216
1484
  if action == "changed" or action == "removed" then
1217
1485
  local relation = select(1, ...) :: Entity
1218
- local callback = select(2, ...) :: (entity: Entity, id: number, value: any) -> ()
1486
+ local callback = select(2, ...) :: (entity: Entity, id: Component, value: any) -> ()
1219
1487
  local hooks = action == "changed" and client.network_hooks.changed or client.network_hooks.removed
1220
1488
  local entries = hooks[relation] :: ChangedHooksEntry
1221
1489
 
@@ -1281,6 +1549,31 @@ function client_replicator.override(client: Client, action: string, ...)
1281
1549
  return disconnect
1282
1550
  end
1283
1551
 
1552
+ function client_replicator.encode_component(client: Client, component: Entity): number
1553
+ client_replicator.resolve_dirty(client)
1554
+ local encoded = client.shared.components.members[component]
1555
+ if not encoded then
1556
+ common.log_error(`attempted to encode a non-shared component `, common.log_component(client.world, component))
1557
+ return 0
1558
+ end
1559
+ return encoded
1560
+ end
1561
+
1562
+ function client_replicator.decode_component(client: Client, encoded: number): Entity
1563
+ client_replicator.resolve_dirty(client)
1564
+ local component = client.shared.components.indexes[encoded]
1565
+ if not component then
1566
+ print("NON SHARED COMPONENT", encoded, client.shared.components)
1567
+ error "attemped to decode a non shared component"
1568
+ end
1569
+ return component
1570
+ end
1571
+
1572
+ function client_replicator.get_shared_count(client: Client): number
1573
+ client_replicator.resolve_dirty(client)
1574
+ return #client.shared.components.keys
1575
+ end
1576
+
1284
1577
  function client_replicator.get_server_entity(client: Client, client_entity: Entity): number?
1285
1578
  return client.client_ids[client_entity]
1286
1579
  end
@@ -1292,7 +1585,7 @@ end
1292
1585
  function client_replicator.register_entity(client: Client, entity: Entity, server_entity: number)
1293
1586
  client.server_ids[server_entity] = entity
1294
1587
  client.client_ids[entity] = server_entity
1295
- client.world:add(entity, client.components.__alive_tracking__)
1588
+ WORLD_ADD(client.world, entity, client.components.__alive_tracking__)
1296
1589
  end
1297
1590
 
1298
1591
  function client_replicator.unregister_entity(client: Client, entity: Entity)
@@ -1308,14 +1601,14 @@ function client_replicator.register_custom_id(client: Client, custom_id: CustomI
1308
1601
  client.is_shared_dirty = true
1309
1602
  end
1310
1603
 
1311
- function client_replicator.generate_handshake(client: Client): common.HandshakeInfo
1604
+ function client_replicator.generate_handshake(client: Client): types.HandshakeInfo
1312
1605
  client_replicator.resolve_dirty(client)
1313
- return utils.generate_handshake(client.world, client.shared)
1606
+ return utils.generate_handshake(client.shared)
1314
1607
  end
1315
1608
 
1316
- function client_replicator.verify_handshake(client: Client, handshake: common.HandshakeInfo): (boolean, string?)
1609
+ function client_replicator.verify_handshake(client: Client, handshake: types.HandshakeInfo): (boolean, string?)
1317
1610
  client_replicator.resolve_dirty(client)
1318
- return utils.verify_handshake(client.world, client.shared, handshake, "server", "client")
1611
+ return utils.verify_handshake(client.shared, handshake, "server", "client")
1319
1612
  end
1320
1613
 
1321
1614
  function client_replicator.destroy(client: Client)
@@ -1332,18 +1625,25 @@ function client_replicator.handle_global(client: Client, handler: (id: number) -
1332
1625
  client.global_handler = handler
1333
1626
  end
1334
1627
 
1335
- local function create(world: World?, components: common.Components): Client
1628
+ local function create(world: World?, components: types.Components?): Client
1336
1629
  local self = {} :: Client
1337
1630
 
1338
- self.components = components
1339
- self.ordered_creation = true
1631
+ if components then
1632
+ self.components = components
1633
+ elseif world then
1634
+ self.components = utils.create_components(utils.tag_factory(world), utils.component_factory(world))
1635
+ utils.add_component_names(self.components, function(component, name)
1636
+ common.add_name(world, component, name)
1637
+ end)
1638
+ end
1639
+
1340
1640
  self.server_ids = {}
1341
1641
  self.client_ids = {}
1342
1642
  self.is_shared_dirty = false
1643
+ self.component_serdes = {}
1343
1644
  self.requires_shared_lookup = {
1344
1645
  self.components.shared,
1345
- self.components.serdes,
1346
- jecs.Name,
1646
+ ECS_NAME,
1347
1647
  }
1348
1648
  self.global_handler = nil
1349
1649
  self.world = world :: any