@rbxts/replecs 0.1.0-alpha7 → 0.1.0-alpha9-test5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/replecs/client.luau +471 -64
- package/out/replecs/index.d.ts +47 -7
- package/out/replecs/init.luau +3 -1
- package/out/replecs/masking/filter_group.luau +1 -0
- package/out/replecs/masking/init.luau +264 -93
- package/out/replecs/masking/mask_generator.luau +11 -5
- package/out/replecs/server.luau +256 -75
- package/out/replecs/utils.luau +0 -30
- package/package.json +2 -1
package/out/replecs/client.luau
CHANGED
|
@@ -9,8 +9,43 @@ local cursor = utils.cursor
|
|
|
9
9
|
type Cursor = utils.Cursor
|
|
10
10
|
|
|
11
11
|
type Entity<T = any> = jecs.Entity<T>
|
|
12
|
+
type Id<T = any> = jecs.Id<T>
|
|
12
13
|
type World = jecs.World
|
|
13
14
|
|
|
15
|
+
type Array<T> = { T }
|
|
16
|
+
type Map<K, V> = { [K]: V }
|
|
17
|
+
type Disconnect = () -> ()
|
|
18
|
+
|
|
19
|
+
type HookMethod =
|
|
20
|
+
& ((
|
|
21
|
+
self: Client,
|
|
22
|
+
entity: Entity,
|
|
23
|
+
action: "change",
|
|
24
|
+
relation: Entity,
|
|
25
|
+
callback: (entity: Entity, id: Id, value: any) -> ()
|
|
26
|
+
) -> Disconnect)
|
|
27
|
+
& ((
|
|
28
|
+
self: Client,
|
|
29
|
+
entity: Entity,
|
|
30
|
+
action: "removed",
|
|
31
|
+
relation: Entity,
|
|
32
|
+
callback: (entity: Entity, id: Id) -> ()
|
|
33
|
+
) -> Disconnect)
|
|
34
|
+
& (self: Client, entity: Entity, action: "deleted", callback: (entity: Entity) -> ()) -> Disconnect
|
|
35
|
+
|
|
36
|
+
type ChangedHooksEntry = {
|
|
37
|
+
overrides: boolean,
|
|
38
|
+
callbacks: Array<(entity: Entity, id: Id, value: any) -> ()>,
|
|
39
|
+
}
|
|
40
|
+
type RemovedHooksEntry = {
|
|
41
|
+
overrides: boolean,
|
|
42
|
+
callbacks: Array<(entity: Entity, id: Id) -> ()>,
|
|
43
|
+
}
|
|
44
|
+
type DeletedHookEntry = {
|
|
45
|
+
overrides: boolean,
|
|
46
|
+
callbacks: Array<(entity: Entity) -> ()>,
|
|
47
|
+
}
|
|
48
|
+
|
|
14
49
|
export type Client = {
|
|
15
50
|
world: World,
|
|
16
51
|
inited: boolean?,
|
|
@@ -18,6 +53,8 @@ export type Client = {
|
|
|
18
53
|
is_replicating: boolean,
|
|
19
54
|
after_replication_callbacks: { () -> () },
|
|
20
55
|
|
|
56
|
+
is_dirty: boolean,
|
|
57
|
+
requires_shared_lookup: { Entity },
|
|
21
58
|
components: common.Components,
|
|
22
59
|
shared: common.Shared,
|
|
23
60
|
global_handler: ((id: number) -> Entity)?,
|
|
@@ -28,11 +65,26 @@ export type Client = {
|
|
|
28
65
|
|
|
29
66
|
init: (self: Client, world: World?) -> (),
|
|
30
67
|
destroy: (self: Client) -> (),
|
|
31
|
-
after_replication: (self: Client, callback: () -> ()) -> (),
|
|
32
68
|
handle_global: (self: Client, handler: (id: number) -> Entity) -> (),
|
|
33
69
|
get_server_entity: (self: Client, client_entity: Entity) -> number?,
|
|
34
70
|
get_client_entity: (self: Client, server_entity: number) -> Entity?,
|
|
35
71
|
|
|
72
|
+
hooks: common.WorldHooks,
|
|
73
|
+
hooked: Array<() -> ()>,
|
|
74
|
+
|
|
75
|
+
after_replication: (self: Client, callback: () -> ()) -> (),
|
|
76
|
+
added: (self: Client, callback: (entity: Entity) -> ()) -> Disconnect,
|
|
77
|
+
hook: HookMethod,
|
|
78
|
+
override: HookMethod,
|
|
79
|
+
addition_hooks: Array<(entity: Entity) -> ()>,
|
|
80
|
+
entity_hooks: {
|
|
81
|
+
[Entity]: {
|
|
82
|
+
changed: Map<Entity, ChangedHooksEntry>,
|
|
83
|
+
removed: Map<Entity, RemovedHooksEntry>,
|
|
84
|
+
deleted: DeletedHookEntry?,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
36
88
|
encode_component: (self: Client, component: Entity) -> number,
|
|
37
89
|
decode_component: (self: Client, component: number) -> Entity,
|
|
38
90
|
|
|
@@ -46,7 +98,6 @@ local ENTITY_ID_TYPES = {
|
|
|
46
98
|
entity = 1,
|
|
47
99
|
custom = 2,
|
|
48
100
|
shared = 3,
|
|
49
|
-
global = 4,
|
|
50
101
|
}
|
|
51
102
|
local PACKET_TYPES = {
|
|
52
103
|
full = 1,
|
|
@@ -55,6 +106,7 @@ local PACKET_TYPES = {
|
|
|
55
106
|
}
|
|
56
107
|
|
|
57
108
|
local ECS_NAME = jecs.Name
|
|
109
|
+
local WILDCARD = jecs.Wildcard
|
|
58
110
|
|
|
59
111
|
local function PAIR(first: Entity, second: Entity): Entity
|
|
60
112
|
return jecs.pair(first, second)
|
|
@@ -66,12 +118,106 @@ local function get_or_create_entity(client: Client, server_id: number): Entity
|
|
|
66
118
|
return server_entity
|
|
67
119
|
else
|
|
68
120
|
local entity = client.world:entity()
|
|
121
|
+
client.world:add(entity, client.components.__alive_tracking__)
|
|
122
|
+
|
|
69
123
|
client.server_ids[server_id] = entity
|
|
70
124
|
client.client_ids[entity] = server_id
|
|
125
|
+
|
|
126
|
+
for _, callback in client.addition_hooks do
|
|
127
|
+
callback(entity)
|
|
128
|
+
end
|
|
129
|
+
|
|
71
130
|
return entity
|
|
72
131
|
end
|
|
73
132
|
end
|
|
74
133
|
|
|
134
|
+
local function network_entity_set(client: Client, entity: Entity, id: Id, value: any, changed_hooks: ChangedHooksEntry?)
|
|
135
|
+
if changed_hooks then
|
|
136
|
+
if not changed_hooks.overrides then
|
|
137
|
+
client.world:set(entity, id, value)
|
|
138
|
+
end
|
|
139
|
+
for _, callback in changed_hooks.callbacks do
|
|
140
|
+
callback(entity, id, value)
|
|
141
|
+
end
|
|
142
|
+
else
|
|
143
|
+
client.world:set(entity, id, value)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
local function network_entity_add(client: Client, entity: Entity, component: Entity, changed_hooks: ChangedHooksEntry?)
|
|
148
|
+
if changed_hooks then
|
|
149
|
+
if not changed_hooks.overrides then
|
|
150
|
+
client.world:add(entity, component)
|
|
151
|
+
end
|
|
152
|
+
for _, callback in changed_hooks.callbacks do
|
|
153
|
+
callback(entity, component)
|
|
154
|
+
end
|
|
155
|
+
else
|
|
156
|
+
client.world:add(entity, component)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
local function network_entity_remove(client: Client, entity: Entity, id: Entity, removed_hooks: RemovedHooksEntry?)
|
|
161
|
+
if removed_hooks then
|
|
162
|
+
for _, callback in removed_hooks.callbacks do
|
|
163
|
+
callback(entity, id)
|
|
164
|
+
end
|
|
165
|
+
if not removed_hooks.overrides then
|
|
166
|
+
client.world:remove(entity, id)
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
client.world:remove(entity, id)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
local function network_entity_remove_relations(
|
|
174
|
+
client: Client,
|
|
175
|
+
entity: Entity,
|
|
176
|
+
relation: Entity,
|
|
177
|
+
removed_hooks: RemovedHooksEntry?
|
|
178
|
+
)
|
|
179
|
+
local world = client.world
|
|
180
|
+
local entity_index = jecs.entity_index_try_get_fast(world.entity_index :: any, entity :: any)
|
|
181
|
+
if not entity_index then
|
|
182
|
+
return
|
|
183
|
+
end
|
|
184
|
+
local archetype = entity_index.archetype
|
|
185
|
+
|
|
186
|
+
local wildcard = PAIR(relation, WILDCARD) :: any
|
|
187
|
+
local idr = world.component_index[wildcard]
|
|
188
|
+
if not idr then
|
|
189
|
+
return
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
local archetype_id = archetype.id
|
|
193
|
+
local count = idr.counts[archetype_id]
|
|
194
|
+
if not count then
|
|
195
|
+
return
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
local start = idr.records[archetype_id]
|
|
199
|
+
if not start then
|
|
200
|
+
return
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if removed_hooks then
|
|
204
|
+
for i = start, start + count - 1 do
|
|
205
|
+
local pair = archetype.types[i]
|
|
206
|
+
for _, callback in removed_hooks.callbacks do
|
|
207
|
+
callback(entity, pair)
|
|
208
|
+
end
|
|
209
|
+
if not removed_hooks.overrides then
|
|
210
|
+
world:remove(entity, pair :: any)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
else
|
|
214
|
+
for i = start, start + count - 1 do
|
|
215
|
+
local pair = archetype.types[i]
|
|
216
|
+
world:remove(entity, pair :: any)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
75
221
|
local function encode_component(client: Client, component: Entity): number
|
|
76
222
|
local encoded = client.shared.ids[component]
|
|
77
223
|
if not encoded then
|
|
@@ -89,7 +235,7 @@ local function decode_component(client: Client, encoded: number): Entity
|
|
|
89
235
|
return component
|
|
90
236
|
end
|
|
91
237
|
|
|
92
|
-
local function read_component_id(client: Client, c: Cursor)
|
|
238
|
+
local function read_component_id(client: Client, c: Cursor): Entity
|
|
93
239
|
local shared_id = cursor.readu8(c)
|
|
94
240
|
local component = client.shared.components[shared_id]
|
|
95
241
|
if not component then
|
|
@@ -99,8 +245,7 @@ local function read_component_id(client: Client, c: Cursor)
|
|
|
99
245
|
return component
|
|
100
246
|
end
|
|
101
247
|
|
|
102
|
-
local function read_component_value(client: Client, c: Cursor, variants: { any }?):
|
|
103
|
-
local component = read_component_id(client, c)
|
|
248
|
+
local function read_component_value(client: Client, component: Entity, c: Cursor, variants: { any }?): any
|
|
104
249
|
local serdes = client.shared.serdes[component]
|
|
105
250
|
|
|
106
251
|
if serdes then
|
|
@@ -119,17 +264,23 @@ local function read_component_value(client: Client, c: Cursor, variants: { any }
|
|
|
119
264
|
end
|
|
120
265
|
|
|
121
266
|
local output = serdes.deserialize(appended, serdes_variants)
|
|
122
|
-
return
|
|
267
|
+
return output
|
|
123
268
|
else
|
|
124
269
|
local variant_id = cursor.read_vlq(c)
|
|
125
270
|
if variant_id == 0 then
|
|
126
|
-
return
|
|
271
|
+
return nil
|
|
127
272
|
end
|
|
128
273
|
local value = (variants :: { any })[variant_id]
|
|
129
|
-
return
|
|
274
|
+
return value
|
|
130
275
|
end
|
|
131
276
|
end
|
|
132
277
|
|
|
278
|
+
local function read_component(client: Client, c: Cursor, variants: { any }?): (Entity, any)
|
|
279
|
+
local component = read_component_id(client, c)
|
|
280
|
+
local value = read_component_value(client, component, c, variants)
|
|
281
|
+
return component, value
|
|
282
|
+
end
|
|
283
|
+
|
|
133
284
|
local function resolve_global(client: Client, global_id: number): Entity
|
|
134
285
|
if not client.global_handler then
|
|
135
286
|
error "global id parser not set, consider using client:handle_global()"
|
|
@@ -139,14 +290,14 @@ local function resolve_global(client: Client, global_id: number): Entity
|
|
|
139
290
|
end
|
|
140
291
|
|
|
141
292
|
local function read_entity_id(client: Client, c: Cursor, variants: { any }?): (Entity?, number?)
|
|
142
|
-
local id_type = cursor.
|
|
293
|
+
local id_type = cursor.readu8(c)
|
|
143
294
|
|
|
144
|
-
if id_type <=
|
|
295
|
+
if id_type <= GLOBAL_ID_OFFSET then
|
|
145
296
|
if id_type == ENTITY_ID_TYPES.entity then
|
|
146
297
|
local server_id = cursor.readu40(c)
|
|
147
298
|
return client.server_ids[server_id], server_id
|
|
148
299
|
elseif id_type == ENTITY_ID_TYPES.custom then
|
|
149
|
-
local component, value =
|
|
300
|
+
local component, value = read_component(client, c, variants)
|
|
150
301
|
local custom_getter = client.custom_ids[component]
|
|
151
302
|
if not custom_getter then
|
|
152
303
|
error(
|
|
@@ -159,9 +310,6 @@ local function read_entity_id(client: Client, c: Cursor, variants: { any }?): (E
|
|
|
159
310
|
return custom_getter(value), nil
|
|
160
311
|
elseif id_type == ENTITY_ID_TYPES.shared then
|
|
161
312
|
return read_component_id(client, c), nil
|
|
162
|
-
elseif id_type == ENTITY_ID_TYPES.global then
|
|
163
|
-
local global_id = cursor.readu8(c) - GLOBAL_ID_OFFSET
|
|
164
|
-
return resolve_global(client, global_id), nil
|
|
165
313
|
end
|
|
166
314
|
error(`malformed entity id {id_type} ` .. cursor.readu32(c))
|
|
167
315
|
else
|
|
@@ -178,34 +326,86 @@ local function process_entity_id(client: Client, c: Cursor, variants: { any }?)
|
|
|
178
326
|
end
|
|
179
327
|
end
|
|
180
328
|
|
|
181
|
-
local function
|
|
329
|
+
local function process_entity_relations(
|
|
330
|
+
client: Client,
|
|
331
|
+
entity: jecs.Entity,
|
|
332
|
+
changed_hooks: Map<Entity, ChangedHooksEntry>?,
|
|
333
|
+
c: Cursor,
|
|
334
|
+
variants: { any }?
|
|
335
|
+
)
|
|
336
|
+
local components = client.components
|
|
182
337
|
local relation = read_component_id(client, c)
|
|
183
338
|
local total_targets = cursor.read_vlq(c)
|
|
339
|
+
local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
|
|
184
340
|
|
|
185
341
|
for _ = 1, total_targets do
|
|
186
342
|
local target = process_entity_id(client, c, variants)
|
|
187
|
-
client
|
|
343
|
+
network_entity_add(client, entity, PAIR(relation, target), pair_hooks)
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
local function process_entity_relation_values(
|
|
348
|
+
client: Client,
|
|
349
|
+
entity: jecs.Entity,
|
|
350
|
+
changed_hooks: Map<Entity, ChangedHooksEntry>?,
|
|
351
|
+
c: Cursor,
|
|
352
|
+
variants: { any }?
|
|
353
|
+
)
|
|
354
|
+
local relation = read_component_id(client, c)
|
|
355
|
+
local total_targets = cursor.read_vlq(c)
|
|
356
|
+
local components = client.components
|
|
357
|
+
local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
|
|
358
|
+
|
|
359
|
+
for _ = 1, total_targets do
|
|
360
|
+
local value = read_component_value(client, relation, c, variants)
|
|
361
|
+
local target = process_entity_id(client, c, variants)
|
|
362
|
+
network_entity_set(client, entity, PAIR(relation, target), value, pair_hooks)
|
|
188
363
|
end
|
|
189
364
|
end
|
|
190
365
|
|
|
191
366
|
local function process_entity(client: Client, c: Cursor, variants: { any }?)
|
|
192
367
|
local entity = process_entity_id(client, c, variants)
|
|
368
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
369
|
+
local changed_hooks = entity_hooks and entity_hooks.changed
|
|
370
|
+
local components = client.components
|
|
193
371
|
|
|
194
372
|
local total_tags = cursor.read_vlq(c)
|
|
195
373
|
for _ = 1, total_tags do
|
|
196
374
|
local tag = read_component_id(client, c)
|
|
197
|
-
|
|
375
|
+
local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
|
|
376
|
+
network_entity_add(client, entity, tag, tag_hooks)
|
|
198
377
|
end
|
|
199
378
|
|
|
200
379
|
local total_components = cursor.read_vlq(c)
|
|
201
380
|
for _ = 1, total_components do
|
|
202
|
-
local component, value =
|
|
203
|
-
|
|
381
|
+
local component, value = read_component(client, c, variants)
|
|
382
|
+
local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
|
|
383
|
+
network_entity_set(client, entity, component, value, component_hooks)
|
|
204
384
|
end
|
|
205
385
|
|
|
206
386
|
local total_pairs = cursor.read_vlq(c)
|
|
207
387
|
for _ = 1, total_pairs do
|
|
208
|
-
|
|
388
|
+
process_entity_relations(client, entity, changed_hooks, c, variants)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
local total_pairs_values = cursor.read_vlq(c)
|
|
392
|
+
for _ = 1, total_pairs_values do
|
|
393
|
+
process_entity_relation_values(client, entity, changed_hooks, c, variants)
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
local function finish_replication(client: Client)
|
|
398
|
+
client.is_replicating = false
|
|
399
|
+
for _, callback in client.after_replication_callbacks do
|
|
400
|
+
callback()
|
|
401
|
+
end
|
|
402
|
+
table.clear(client.after_replication_callbacks)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
local function resolve_dirty(client: Client)
|
|
406
|
+
if client.is_dirty then
|
|
407
|
+
client.shared = utils.create_shared_lookup(client.world, client.components)
|
|
408
|
+
client.is_dirty = false :: true -- wtf
|
|
209
409
|
end
|
|
210
410
|
end
|
|
211
411
|
|
|
@@ -236,7 +436,10 @@ end
|
|
|
236
436
|
local function apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
237
437
|
local c = cursor.from(buf)
|
|
238
438
|
check_packet_type(c, "reliable")
|
|
439
|
+
resolve_dirty(client)
|
|
440
|
+
client.is_replicating = true
|
|
239
441
|
|
|
442
|
+
local components = client.components
|
|
240
443
|
local total_packets = cursor.read_vlq(c)
|
|
241
444
|
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
242
445
|
|
|
@@ -254,21 +457,31 @@ local function apply_updates(client: Client, buf: buffer, all_variants: { { any
|
|
|
254
457
|
local entity = process_entity_id(client, c, variants)
|
|
255
458
|
local added_mask = cursor.readu8(c)
|
|
256
459
|
|
|
460
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
461
|
+
local changed_hooks = entity_hooks and entity_hooks.changed
|
|
462
|
+
|
|
257
463
|
local total_tags = read_vlq_bitmask(c, added_mask, 0)
|
|
258
464
|
for _ = 1, total_tags do
|
|
259
465
|
local tag = read_component_id(client, c)
|
|
260
|
-
|
|
466
|
+
local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
|
|
467
|
+
network_entity_add(client, entity, tag, tag_hooks)
|
|
261
468
|
end
|
|
262
469
|
|
|
263
470
|
local total_components = read_vlq_bitmask(c, added_mask, 1)
|
|
264
471
|
for _ = 1, total_components do
|
|
265
|
-
local component, value =
|
|
266
|
-
|
|
472
|
+
local component, value = read_component(client, c, variants)
|
|
473
|
+
local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
|
|
474
|
+
network_entity_set(client, entity, component, value, component_hooks)
|
|
267
475
|
end
|
|
268
476
|
|
|
269
477
|
local total_pairs = read_vlq_bitmask(c, added_mask, 2)
|
|
270
478
|
for _ = 1, total_pairs do
|
|
271
|
-
|
|
479
|
+
process_entity_relations(client, entity, changed_hooks, c, variants)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
local total_pairs_values = read_vlq_bitmask(c, added_mask, 3)
|
|
483
|
+
for _ = 1, total_pairs_values do
|
|
484
|
+
process_entity_relation_values(client, entity, changed_hooks, c, variants)
|
|
272
485
|
end
|
|
273
486
|
end
|
|
274
487
|
|
|
@@ -277,68 +490,99 @@ local function apply_updates(client: Client, buf: buffer, all_variants: { { any
|
|
|
277
490
|
local entity = process_entity_id(client, c, variants)
|
|
278
491
|
local changed_mask = cursor.readu8(c)
|
|
279
492
|
|
|
493
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
494
|
+
local changed_hooks = entity_hooks and entity_hooks.changed
|
|
495
|
+
local removed_hooks = entity_hooks and entity_hooks.removed
|
|
496
|
+
|
|
280
497
|
local total_tagged = read_vlq_bitmask(c, changed_mask, 0)
|
|
281
498
|
for _ = 1, total_tagged do
|
|
282
499
|
local tag = read_component_id(client, c)
|
|
283
|
-
|
|
500
|
+
local tag_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, tag)]
|
|
501
|
+
network_entity_add(client, entity, tag, tag_hooks)
|
|
284
502
|
end
|
|
285
503
|
|
|
286
504
|
local total_components = read_vlq_bitmask(c, changed_mask, 1)
|
|
287
505
|
for _ = 1, total_components do
|
|
288
|
-
local component, value =
|
|
289
|
-
|
|
506
|
+
local component, value = read_component(client, c, variants)
|
|
507
|
+
local component_hooks = changed_hooks and changed_hooks[PAIR(components.reliable, component)]
|
|
508
|
+
network_entity_set(client, entity, component, value, component_hooks)
|
|
290
509
|
end
|
|
291
510
|
|
|
292
|
-
local
|
|
511
|
+
local total_removed = read_vlq_bitmask(c, changed_mask, 2)
|
|
512
|
+
for _ = 1, total_removed do
|
|
513
|
+
local component = read_component_id(client, c)
|
|
514
|
+
local component_hooks = removed_hooks and removed_hooks[PAIR(components.reliable, component)]
|
|
515
|
+
network_entity_remove(client, entity, component, component_hooks)
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
local total_pairs = read_vlq_bitmask(c, changed_mask, 3)
|
|
293
519
|
for _ = 1, total_pairs do
|
|
294
520
|
local relation = read_component_id(client, c)
|
|
521
|
+
local pair_changed_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
|
|
522
|
+
local pair_removed_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
|
|
295
523
|
local total_targets = cursor.read_vlq(c)
|
|
296
524
|
|
|
297
525
|
for _ = 1, total_targets do
|
|
298
|
-
local
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
526
|
+
local action_type = cursor.readi8(c)
|
|
527
|
+
|
|
528
|
+
if action_type == 1 then -- add pair
|
|
529
|
+
local target = process_entity_id(client, c, variants)
|
|
530
|
+
network_entity_add(client, entity, PAIR(relation, target), pair_changed_hooks)
|
|
531
|
+
elseif action_type == -1 then -- remove pair
|
|
532
|
+
local target = read_entity_id(client, c, variants)
|
|
533
|
+
if target then
|
|
534
|
+
network_entity_remove(client, entity, PAIR(relation, target), pair_removed_hooks)
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
305
539
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
540
|
+
local total_pairs_values_removed = read_vlq_bitmask(c, changed_mask, 4)
|
|
541
|
+
for _ = 1, total_pairs_values_removed do
|
|
542
|
+
local relation = read_component_id(client, c)
|
|
543
|
+
local pair_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
|
|
544
|
+
local total_targets = cursor.read_vlq(c)
|
|
545
|
+
|
|
546
|
+
for _ = 1, total_targets do
|
|
547
|
+
local target = read_entity_id(client, c, variants)
|
|
548
|
+
if target then
|
|
549
|
+
network_entity_remove(client, entity, PAIR(relation, target), pair_hooks)
|
|
310
550
|
end
|
|
311
551
|
end
|
|
312
552
|
end
|
|
313
553
|
|
|
314
|
-
local
|
|
315
|
-
for _ = 1,
|
|
316
|
-
local
|
|
317
|
-
|
|
554
|
+
local total_pairs_values = read_vlq_bitmask(c, changed_mask, 5)
|
|
555
|
+
for _ = 1, total_pairs_values do
|
|
556
|
+
local relation = read_component_id(client, c)
|
|
557
|
+
local pair_hooks = changed_hooks and changed_hooks[PAIR(components.pair, relation)]
|
|
558
|
+
local total_targets = cursor.read_vlq(c)
|
|
559
|
+
|
|
560
|
+
for _ = 1, total_targets do
|
|
561
|
+
local value = read_component_value(client, relation, c, variants)
|
|
562
|
+
local target = process_entity_id(client, c, variants)
|
|
563
|
+
network_entity_set(client, entity, PAIR(relation, target), value, pair_hooks)
|
|
564
|
+
end
|
|
318
565
|
end
|
|
319
566
|
end
|
|
320
567
|
|
|
321
|
-
local
|
|
322
|
-
for _ = 1,
|
|
568
|
+
local total_component_deletions = read_vlq_bitmask(c, storage_mask, 3)
|
|
569
|
+
for _ = 1, total_component_deletions do
|
|
323
570
|
local entity = process_entity_id(client, c, variants)
|
|
324
|
-
local
|
|
325
|
-
|
|
326
|
-
local total_tags = read_vlq_bitmask(c, deleted_mask, 0)
|
|
327
|
-
for _ = 1, total_tags do
|
|
328
|
-
local tag = read_component_id(client, c)
|
|
329
|
-
client.world:remove(entity, tag)
|
|
330
|
-
end
|
|
571
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
572
|
+
local removed_hooks = entity_hooks and entity_hooks.removed
|
|
331
573
|
|
|
332
|
-
local
|
|
333
|
-
for _ = 1,
|
|
574
|
+
local total_removed = cursor.read_vlq(c)
|
|
575
|
+
for _ = 1, total_removed do
|
|
334
576
|
local component = read_component_id(client, c)
|
|
335
|
-
|
|
577
|
+
local component_hooks = removed_hooks and removed_hooks[PAIR(components.reliable, component)]
|
|
578
|
+
network_entity_remove(client, entity, component, component_hooks)
|
|
336
579
|
end
|
|
337
580
|
|
|
338
|
-
local total_pairs =
|
|
581
|
+
local total_pairs = cursor.read_vlq(c)
|
|
339
582
|
for _ = 1, total_pairs do
|
|
340
583
|
local relation = read_component_id(client, c)
|
|
341
|
-
|
|
584
|
+
local pair_hooks = removed_hooks and removed_hooks[PAIR(components.pair, relation)]
|
|
585
|
+
network_entity_remove_relations(client, entity, relation, pair_hooks)
|
|
342
586
|
end
|
|
343
587
|
end
|
|
344
588
|
|
|
@@ -346,19 +590,35 @@ local function apply_updates(client: Client, buf: buffer, all_variants: { { any
|
|
|
346
590
|
for _ = 1, total_deleted do
|
|
347
591
|
local entity, server_id = read_entity_id(client, c, variants)
|
|
348
592
|
if entity then
|
|
349
|
-
client.
|
|
593
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
594
|
+
local deleted_hooks = entity_hooks and entity_hooks.deleted
|
|
595
|
+
if deleted_hooks then
|
|
596
|
+
for _, callback in deleted_hooks.callbacks do
|
|
597
|
+
callback(entity)
|
|
598
|
+
end
|
|
599
|
+
if not deleted_hooks.overrides then
|
|
600
|
+
client.world:delete(entity)
|
|
601
|
+
end
|
|
602
|
+
else
|
|
603
|
+
client.world:delete(entity)
|
|
604
|
+
end
|
|
350
605
|
end
|
|
351
606
|
if server_id then
|
|
352
607
|
client.server_ids[server_id] = nil
|
|
353
608
|
end
|
|
354
609
|
end
|
|
355
610
|
end
|
|
611
|
+
|
|
612
|
+
finish_replication(client)
|
|
356
613
|
end
|
|
357
614
|
|
|
358
615
|
local function apply_unreliable(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
359
616
|
local c = cursor.from(buf)
|
|
360
617
|
check_packet_type(c, "unreliable")
|
|
618
|
+
resolve_dirty(client)
|
|
619
|
+
client.is_replicating = true
|
|
361
620
|
|
|
621
|
+
local components = client.components
|
|
362
622
|
local total_packets = cursor.read_vlq(c)
|
|
363
623
|
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
364
624
|
|
|
@@ -368,25 +628,36 @@ local function apply_unreliable(client: Client, buf: buffer, all_variants: { { a
|
|
|
368
628
|
|
|
369
629
|
for _ = 1, total_entities do
|
|
370
630
|
local entity = read_entity_id(client, c, variants)
|
|
631
|
+
|
|
371
632
|
local total_unreliable = cursor.read_vlq(c)
|
|
372
633
|
|
|
373
634
|
if entity then
|
|
374
|
-
local
|
|
375
|
-
|
|
635
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
636
|
+
local changed_hooks = entity_hooks and entity_hooks.changed
|
|
637
|
+
|
|
638
|
+
for _ = 1, total_unreliable do
|
|
639
|
+
local component_id, value = read_component(client, c, variants)
|
|
640
|
+
local unreliable_hooks = changed_hooks and changed_hooks[PAIR(components.unreliable, component_id)]
|
|
641
|
+
network_entity_set(client, entity, component_id, value, unreliable_hooks)
|
|
642
|
+
end
|
|
376
643
|
else
|
|
377
644
|
-- dont apply unreliable updates to entities that dont exist in the client yet
|
|
378
645
|
-- but we still need to process the values to keep the cursor in the right place
|
|
379
646
|
for _ = 1, total_unreliable do
|
|
380
|
-
|
|
647
|
+
read_component(client, c, variants)
|
|
381
648
|
end
|
|
382
649
|
end
|
|
383
650
|
end
|
|
384
651
|
end
|
|
652
|
+
|
|
653
|
+
finish_replication(client)
|
|
385
654
|
end
|
|
386
655
|
|
|
387
656
|
local function apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
388
657
|
local c = cursor.from(buf)
|
|
389
658
|
check_packet_type(c, "full")
|
|
659
|
+
resolve_dirty(client)
|
|
660
|
+
client.is_replicating = true
|
|
390
661
|
|
|
391
662
|
local total_packets = cursor.read_vlq(c)
|
|
392
663
|
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
@@ -399,6 +670,8 @@ local function apply_full(client: Client, buf: buffer, all_variants: { { any } }
|
|
|
399
670
|
process_entity(client, c, variants)
|
|
400
671
|
end
|
|
401
672
|
end
|
|
673
|
+
|
|
674
|
+
finish_replication(client)
|
|
402
675
|
end
|
|
403
676
|
|
|
404
677
|
local function init(client: Client, _world: World?)
|
|
@@ -417,6 +690,13 @@ local function init(client: Client, _world: World?)
|
|
|
417
690
|
error "Providing a world is required to start replecs"
|
|
418
691
|
end
|
|
419
692
|
|
|
693
|
+
local hooks = {
|
|
694
|
+
added = (world :: any).added,
|
|
695
|
+
changed = (world :: any).changed,
|
|
696
|
+
removed = (world :: any).removed,
|
|
697
|
+
}
|
|
698
|
+
client.hooks = hooks
|
|
699
|
+
|
|
420
700
|
local components = client.components
|
|
421
701
|
|
|
422
702
|
client.shared = utils.create_shared_lookup(world, components)
|
|
@@ -424,6 +704,27 @@ local function init(client: Client, _world: World?)
|
|
|
424
704
|
for component, custom_get in world:query(components.custom_handler):with(components.shared, ECS_NAME) do
|
|
425
705
|
client.custom_ids[component] = custom_get
|
|
426
706
|
end
|
|
707
|
+
|
|
708
|
+
local alive_unhook = hooks.removed(world, components.__alive_tracking__, function(entity)
|
|
709
|
+
local server_id = client.client_ids[entity]
|
|
710
|
+
if server_id then
|
|
711
|
+
client.server_ids[server_id] = nil
|
|
712
|
+
client.client_ids[entity] = nil
|
|
713
|
+
end
|
|
714
|
+
client.entity_hooks[entity] = nil
|
|
715
|
+
end)
|
|
716
|
+
table.insert(client.hooked, alive_unhook)
|
|
717
|
+
|
|
718
|
+
for _, component in client.requires_shared_lookup do
|
|
719
|
+
local added_hook = hooks.added(world, component, function()
|
|
720
|
+
client.is_dirty = true
|
|
721
|
+
end)
|
|
722
|
+
local removed_hook = hooks.removed(world, component, function()
|
|
723
|
+
client.is_dirty = true
|
|
724
|
+
end)
|
|
725
|
+
table.insert(client.hooked, added_hook)
|
|
726
|
+
table.insert(client.hooked, removed_hook)
|
|
727
|
+
end
|
|
427
728
|
end
|
|
428
729
|
|
|
429
730
|
local function after_replication(client: Client, callback: () -> ())
|
|
@@ -434,6 +735,97 @@ local function after_replication(client: Client, callback: () -> ())
|
|
|
434
735
|
end
|
|
435
736
|
end
|
|
436
737
|
|
|
738
|
+
local function added(client: Client, callback: (entity: Entity) -> ())
|
|
739
|
+
table.insert(client.addition_hooks, callback)
|
|
740
|
+
return function()
|
|
741
|
+
local index = table.find(client.addition_hooks, callback)
|
|
742
|
+
if index then
|
|
743
|
+
table.remove(client.addition_hooks, index)
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
local function get_or_create_entity_hooks(client: Client, entity: Entity)
|
|
749
|
+
local entity_hooks = client.entity_hooks[entity]
|
|
750
|
+
if not entity_hooks then
|
|
751
|
+
entity_hooks = {
|
|
752
|
+
changed = {},
|
|
753
|
+
removed = {},
|
|
754
|
+
deleted = nil :: DeletedHookEntry?,
|
|
755
|
+
}
|
|
756
|
+
client.entity_hooks[entity] = entity_hooks
|
|
757
|
+
end
|
|
758
|
+
return entity_hooks
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
local function add_hook_entry(client: Client, entity: Entity, action: string, ...): ({ overrides: boolean }, () -> ())
|
|
762
|
+
if action == "changed" or action == "removed" then
|
|
763
|
+
local relation = select(1, ...) :: Entity
|
|
764
|
+
local callback = select(2, ...) :: (entity: Entity, id: number, value: any) -> ()
|
|
765
|
+
local hooks = get_or_create_entity_hooks(client, entity)[action]
|
|
766
|
+
local entries = hooks[relation] :: ChangedHooksEntry
|
|
767
|
+
|
|
768
|
+
if not entries then
|
|
769
|
+
entries = {
|
|
770
|
+
overrides = false,
|
|
771
|
+
callbacks = {},
|
|
772
|
+
}
|
|
773
|
+
hooks[relation] = entries
|
|
774
|
+
end
|
|
775
|
+
table.insert(entries.callbacks, callback)
|
|
776
|
+
|
|
777
|
+
local function disconnect()
|
|
778
|
+
local callbacks = entries.callbacks
|
|
779
|
+
local index = table.find(callbacks, callback)
|
|
780
|
+
if index then
|
|
781
|
+
table.remove(callbacks, index)
|
|
782
|
+
if #callbacks == 0 then
|
|
783
|
+
hooks[relation] = nil
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
return entries, disconnect
|
|
788
|
+
elseif action == "deleted" then
|
|
789
|
+
local callback = select(1, ...) :: (entity: Entity) -> ()
|
|
790
|
+
local hooks = get_or_create_entity_hooks(client, entity)
|
|
791
|
+
local entries = hooks.deleted :: DeletedHookEntry
|
|
792
|
+
|
|
793
|
+
if not entries then
|
|
794
|
+
entries = {
|
|
795
|
+
overrides = false,
|
|
796
|
+
callbacks = {},
|
|
797
|
+
}
|
|
798
|
+
hooks.deleted = entries
|
|
799
|
+
end
|
|
800
|
+
table.insert(entries.callbacks, callback)
|
|
801
|
+
|
|
802
|
+
local function disconnect()
|
|
803
|
+
local callbacks = entries.callbacks
|
|
804
|
+
local index = table.find(callbacks, callback)
|
|
805
|
+
if index then
|
|
806
|
+
table.remove(callbacks, index)
|
|
807
|
+
if #callbacks == 0 then
|
|
808
|
+
hooks.deleted = nil
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
end
|
|
812
|
+
return entries, disconnect
|
|
813
|
+
else
|
|
814
|
+
error("invalid hook action: " .. action)
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
local function hook(client: Client, entity: Entity, action: string, ...)
|
|
819
|
+
local entry, disconnect = add_hook_entry(client, entity, action, ...)
|
|
820
|
+
entry.overrides = false
|
|
821
|
+
return disconnect
|
|
822
|
+
end
|
|
823
|
+
local function override(client: Client, entity: Entity, action: string, ...)
|
|
824
|
+
local entry, disconnect = add_hook_entry(client, entity, action, ...)
|
|
825
|
+
entry.overrides = true
|
|
826
|
+
return disconnect
|
|
827
|
+
end
|
|
828
|
+
|
|
437
829
|
local function get_server_entity(client: Client, client_entity: Entity): number?
|
|
438
830
|
return client.client_ids[client_entity]
|
|
439
831
|
end
|
|
@@ -447,8 +839,9 @@ local function destroy(client: Client)
|
|
|
447
839
|
return warn "attempted to destroy a client twice"
|
|
448
840
|
end
|
|
449
841
|
client.inited = nil :: any
|
|
450
|
-
|
|
451
|
-
|
|
842
|
+
for _, unhook in client.hooked do
|
|
843
|
+
unhook()
|
|
844
|
+
end
|
|
452
845
|
end
|
|
453
846
|
|
|
454
847
|
local function handle_global(client: Client, handler: (id: number) -> Entity)
|
|
@@ -459,7 +852,6 @@ local client = {}
|
|
|
459
852
|
client.__index = client
|
|
460
853
|
client.init = init
|
|
461
854
|
client.destroy = destroy
|
|
462
|
-
client.after_replication = after_replication
|
|
463
855
|
client.apply_updates = apply_updates
|
|
464
856
|
client.apply_unreliable = apply_unreliable
|
|
465
857
|
client.apply_full = apply_full
|
|
@@ -468,6 +860,10 @@ client.decode_component = decode_component
|
|
|
468
860
|
client.get_server_entity = get_server_entity
|
|
469
861
|
client.get_client_entity = get_client_entity
|
|
470
862
|
client.handle_global = handle_global
|
|
863
|
+
client.after_replication = after_replication
|
|
864
|
+
client.hook = hook
|
|
865
|
+
client.override = override
|
|
866
|
+
client.added = added
|
|
471
867
|
|
|
472
868
|
local function create(world: World?, components: common.Components): Client
|
|
473
869
|
local self = {} :: Client
|
|
@@ -477,10 +873,21 @@ local function create(world: World?, components: common.Components): Client
|
|
|
477
873
|
self.custom_ids = {}
|
|
478
874
|
self.server_ids = {}
|
|
479
875
|
self.client_ids = {}
|
|
876
|
+
self.is_dirty = false
|
|
877
|
+
self.requires_shared_lookup = {
|
|
878
|
+
self.components.shared,
|
|
879
|
+
self.components.serdes,
|
|
880
|
+
self.components.bytespan,
|
|
881
|
+
jecs.Name,
|
|
882
|
+
}
|
|
480
883
|
self.global_handler = nil
|
|
481
884
|
self.world = world :: any
|
|
482
885
|
self.inited = false
|
|
483
886
|
self.is_replicating = false
|
|
887
|
+
self.hooked = {}
|
|
888
|
+
|
|
889
|
+
self.addition_hooks = {}
|
|
890
|
+
self.entity_hooks = {}
|
|
484
891
|
self.after_replication_callbacks = {}
|
|
485
892
|
|
|
486
893
|
return setmetatable(self, client) :: any
|