@rbxts/replecs 0.2.3 → 0.3.0

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