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