@rbxts/replecs 0.0.1-rc.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.
@@ -0,0 +1,1191 @@
1
+ local jecs = require(script.Parent.Parent.jecs)
2
+ local utils = require(script.Parent.utils)
3
+ local mask_generator = require(script.mask_generator)
4
+
5
+ type Entity<T = any> = jecs.Entity<T>
6
+
7
+ type Bitmask = utils.Bitmask
8
+ type MaskGenerator = mask_generator.MaskGenerator
9
+
10
+ type MemberFilter = Set<Member>
11
+ type Member = any
12
+ type MaskHash = string
13
+
14
+ type Set<T> = { [T]: boolean }
15
+ type Map<K, V> = { [K]: V }
16
+ type Array<T> = { T }
17
+
18
+ type ComponentIndex<T> = {
19
+ [number]: { [Entity]: T },
20
+ }
21
+ type EntityComponentIndex<T> = {
22
+ [Entity]: ComponentIndex<T>,
23
+ }
24
+
25
+ type Lookup = {
26
+ generator: MaskGenerator,
27
+ -- filter in component lookups will never be nil
28
+ filter: MemberFilter?,
29
+ storage_group: StorageGroup,
30
+ }
31
+ type ComponentLookup = Lookup & {
32
+ band_generator: MaskGenerator,
33
+ }
34
+
35
+ export type EntityChanges = {
36
+ tagged: { [Entity]: boolean },
37
+ component: { [Entity]: any },
38
+ removed: { [Entity]: boolean },
39
+ pairs: { [Entity]: Set<Entity> },
40
+ }
41
+
42
+ type LookupsList = {
43
+ entities: Map<Entity, Lookup>,
44
+ components: EntityComponentIndex<ComponentLookup>,
45
+
46
+ deletions: {
47
+ entities: Map<Entity, Lookup>,
48
+ components: EntityComponentIndex<Lookup>,
49
+ },
50
+
51
+ posponed: EntityComponentIndex<MemberFilter>,
52
+ }
53
+
54
+ export type ActiveEntity = {
55
+ components: ComponentIndex<boolean>,
56
+ component_count: number,
57
+ is_entity_mask: boolean,
58
+ }
59
+
60
+ type MaskInfo = {
61
+ bitmask: Bitmask,
62
+ hash: MaskHash,
63
+ members: Array<Member>,
64
+ }
65
+
66
+ type StorageGroup = {
67
+ mask: MaskInfo,
68
+ shared_with: EntityComponentIndex<boolean>,
69
+ is_empty: boolean,
70
+
71
+ deletions: {
72
+ entities: Set<Entity>,
73
+ components: EntityComponentIndex<boolean>,
74
+ },
75
+ deletions_count: number,
76
+
77
+ active: Map<Entity, ActiveEntity>, -- set of active entities
78
+ active_count: number,
79
+
80
+ changes: {
81
+ added: Map<Entity, boolean>,
82
+ changed: Map<Entity, EntityChanges>,
83
+ },
84
+ }
85
+
86
+ type Storages = {
87
+ [MaskHash]: StorageGroup,
88
+ }
89
+
90
+ export type MaskingController = {
91
+ member_count: number,
92
+ member_indexes: { [Member]: number },
93
+ unreplicated: Set<Member>,
94
+
95
+ all_members_generator: MaskGenerator,
96
+ active_members_generator: MaskGenerator,
97
+ unactive_members_generator: MaskGenerator,
98
+
99
+ storages: Storages,
100
+ lookups: LookupsList,
101
+
102
+ register_member: (self: MaskingController, member: Member) -> (),
103
+ unregister_member: (self: MaskingController, member: Member) -> (),
104
+ activate_member: (self: MaskingController, member: Member) -> (),
105
+ member_is_active: (self: MaskingController, member: Member) -> boolean,
106
+
107
+ register_stop_entity: (self: MaskingController, entity: Entity) -> (),
108
+ register_stop_component: (
109
+ self: MaskingController,
110
+ entity: Entity,
111
+ component: Entity,
112
+ component_type: number
113
+ ) -> (),
114
+ unregister_stop_entity: (self: MaskingController, entity: Entity) -> (),
115
+ unregister_stop_component: (
116
+ self: MaskingController,
117
+ entity: Entity,
118
+ component: Entity,
119
+ component_type: number
120
+ ) -> (),
121
+
122
+ propagate_entity_addition: (self: MaskingController, entity: Entity) -> (),
123
+
124
+ start_entity: (self: MaskingController, entity: Entity, filter: MemberFilter?) -> (),
125
+ set_entity: (self: MaskingController, entity: Entity, filter: MemberFilter?) -> (),
126
+ stop_entity: (self: MaskingController, entity: Entity) -> (),
127
+
128
+ start_component: (
129
+ self: MaskingController,
130
+ entity: Entity,
131
+ component: Entity,
132
+ component_type: number,
133
+ filter: MemberFilter?
134
+ ) -> (),
135
+ set_component: (
136
+ self: MaskingController,
137
+ entity: Entity,
138
+ component: Entity,
139
+ component_type: number,
140
+ filter: MemberFilter?
141
+ ) -> (),
142
+ stop_component: (self: MaskingController, entity: Entity, component: Entity, component_type: number) -> (),
143
+
144
+ allocate_entity_addition: (
145
+ self: MaskingController,
146
+ entity: Entity,
147
+ component: Entity,
148
+ component_type: number
149
+ ) -> (),
150
+
151
+ allocate_component_change: (self: MaskingController, entity: Entity, component: Entity, value: any) -> (),
152
+ allocate_tag_addition: (self: MaskingController, entity: Entity, tag: Entity) -> (),
153
+ allocate_pair_addition: (self: MaskingController, entity: Entity, relation: Entity, target: Entity) -> (),
154
+
155
+ allocate_component_removal: (self: MaskingController, entity: Entity, component: Entity) -> (),
156
+ allocate_tag_removal: (self: MaskingController, entity: Entity, tag: Entity) -> (),
157
+ allocate_pair_removal: (self: MaskingController, entity: Entity, relation: Entity, target: Entity) -> (),
158
+ }
159
+
160
+ local COMPONENT_TYPES = {
161
+ component = 1,
162
+ tag = 2,
163
+ pair = 3,
164
+ unreliable = 4,
165
+ }
166
+
167
+ local masking_controller = {}
168
+ masking_controller.__index = masking_controller
169
+
170
+ export type MaskingInternal = MaskingController & typeof(masking_controller)
171
+
172
+ function masking_controller.get_members_from_bitmask(controller: MaskingInternal, bitmask: Bitmask): Array<Member>
173
+ local members: { Member } = {}
174
+ for player, index in controller.member_indexes do
175
+ if bitmask:get(index) then
176
+ table.insert(members, player :: any)
177
+ end
178
+ end
179
+ return members
180
+ end
181
+
182
+ local function create_component_indexes<T>()
183
+ return { {}, {}, {}, {} } :: ComponentIndex<T>
184
+ end
185
+
186
+ local function create_entity_changes(): EntityChanges
187
+ return {
188
+ tagged = {},
189
+ component = {},
190
+ removed = {},
191
+ pairs = {},
192
+ }
193
+ end
194
+
195
+ local function get_or_set_changes(storage: StorageGroup, entity: Entity)
196
+ local changes: EntityChanges = storage.changes.changed[entity]
197
+ if changes == nil then
198
+ changes = create_entity_changes()
199
+ storage.changes.changed[entity] = changes
200
+ end
201
+
202
+ return changes
203
+ end
204
+
205
+ function masking_controller.create_new_storage(
206
+ controller: MaskingInternal,
207
+ bitmask: Bitmask,
208
+ hash: string?,
209
+ members: Array<Member>?
210
+ ): StorageGroup
211
+ return {
212
+ mask = {
213
+ bitmask = bitmask,
214
+ hash = hash or bitmask:tostring(),
215
+ members = members or controller:get_members_from_bitmask(bitmask),
216
+ },
217
+ is_empty = true,
218
+ shared_with = {},
219
+
220
+ changes = {
221
+ added = {},
222
+ changed = {},
223
+ },
224
+
225
+ active = {},
226
+ active_count = 0,
227
+
228
+ deletions = {
229
+ entities = {},
230
+ components = {},
231
+ },
232
+ deletions_count = 0,
233
+ }
234
+ end
235
+
236
+ local function is_include_filter(filter: MemberFilter): boolean
237
+ for player, allow in filter do
238
+ if allow then
239
+ return true
240
+ else
241
+ return false
242
+ end
243
+ end
244
+ return true
245
+ end
246
+ local function create_new_active(): ActiveEntity
247
+ return {
248
+ components = create_component_indexes() :: ComponentIndex<boolean>,
249
+ component_count = 0,
250
+ is_entity_mask = false,
251
+ }
252
+ end
253
+
254
+ local function create_new_changes(): EntityChanges
255
+ return {
256
+ tagged = {},
257
+ component = {},
258
+ removed = {},
259
+ pairs = {},
260
+ }
261
+ end
262
+
263
+ function masking_controller.get_or_create_storage_group(masking: MaskingInternal, bitmask: Bitmask): StorageGroup
264
+ local hash = bitmask:tostring()
265
+ local storage = masking.storages[hash]
266
+
267
+ if not storage then
268
+ storage = masking:create_new_storage(bitmask, hash)
269
+ masking.storages[hash] = storage
270
+ end
271
+ return storage
272
+ end
273
+
274
+ local function get_component_index_entry<T>(
275
+ index: EntityComponentIndex<T>,
276
+ entity: Entity,
277
+ component: Entity,
278
+ component_type: number
279
+ ): T?
280
+ local entity_index = index[entity]
281
+ if entity_index == nil then
282
+ return nil
283
+ end
284
+ local component_index = entity_index[component_type]
285
+
286
+ return component_index and component_index[component] :: T?
287
+ end
288
+
289
+ local function set_component_index_entry<T>(
290
+ index: EntityComponentIndex<T>,
291
+ entity: Entity,
292
+ component: Entity,
293
+ component_type: number,
294
+ value: T
295
+ )
296
+ local entity_index = index[entity]
297
+ if entity_index == nil then
298
+ entity_index = create_component_indexes()
299
+ index[entity] = entity_index
300
+ end
301
+ entity_index[component_type][component] = value
302
+ end
303
+
304
+ local function remove_component_index_entry(
305
+ index: EntityComponentIndex<any>,
306
+ entity: Entity,
307
+ component: Entity,
308
+ component_type: number
309
+ )
310
+ local entity_index = index[entity]
311
+ if entity_index == nil then
312
+ return
313
+ end
314
+ entity_index[component_type][component] = nil
315
+ end
316
+
317
+ masking_controller.get_component_index_entry = get_component_index_entry
318
+ masking_controller.set_component_index_entry = set_component_index_entry
319
+ masking_controller.remove_component_index_entry = remove_component_index_entry
320
+
321
+ local function get_or_set_changed(storage: StorageGroup, entity: Entity, changed: EntityChanges?): EntityChanges
322
+ local storage_changed = storage.changes.changed[entity]
323
+ if storage_changed == nil then
324
+ storage_changed = changed or create_new_changes()
325
+ storage.changes.changed[entity] = storage_changed
326
+ end
327
+ return storage_changed
328
+ end
329
+
330
+ local function get_or_set_active(storage: StorageGroup, entity: Entity, active: ActiveEntity?): ActiveEntity
331
+ local storage_active = storage.active[entity]
332
+ if storage_active == nil then
333
+ storage_active = active or create_new_active()
334
+ storage.active[entity] = storage_active
335
+ storage.active_count += 1
336
+ end
337
+ return storage_active
338
+ end
339
+
340
+ local function remove_active(storage: StorageGroup, entity: Entity): ActiveEntity
341
+ local active = storage.active[entity]
342
+ if active then
343
+ storage.active[entity] = nil
344
+ storage.active_count -= 1
345
+ -- todo: check if empty
346
+ return active
347
+ end
348
+ return utils.logerror "attempted to remove an entity that wasn't active"
349
+ end
350
+
351
+ local function get_or_set_component_active(active: ActiveEntity, component: Entity, component_type: number)
352
+ local components = active.components[component_type]
353
+ if components[component] == nil then
354
+ components[component] = true
355
+ active.component_count += 1
356
+ end
357
+ end
358
+
359
+ local function remove_component_active(storage: StorageGroup, entity: Entity, component: Entity, component_type: number)
360
+ local active = storage.active[entity]
361
+ if not active then
362
+ return
363
+ end
364
+ local components = active.components[component_type]
365
+
366
+ if components[component] then
367
+ components[component] = nil
368
+ active.component_count -= 1
369
+
370
+ if active.component_count <= 0 and not active.is_entity_mask then
371
+ remove_active(storage, entity)
372
+ end
373
+ end
374
+ end
375
+
376
+ local function dissect_component_actives(
377
+ storage: StorageGroup,
378
+ entity: Entity,
379
+ shared_index: ComponentIndex<boolean>
380
+ ): ActiveEntity?
381
+ local new_active: ActiveEntity
382
+ local active = storage.active[entity]
383
+
384
+ for component_type, components in active.components do
385
+ for component in components do
386
+ local is_shared = shared_index[component_type] and shared_index[component_type][component]
387
+ if is_shared then
388
+ new_active = new_active or create_new_active()
389
+
390
+ get_or_set_component_active(new_active, component, component_type)
391
+ remove_component_active(storage, entity, component, component_type)
392
+ end
393
+ end
394
+ end
395
+ return new_active
396
+ end
397
+ function dissect_component_changes(changes: EntityChanges, shared_index: ComponentIndex<boolean>)
398
+ local new_changes: EntityChanges
399
+
400
+ for component, change in changes.component do
401
+ local is_shared = shared_index[COMPONENT_TYPES.component] and shared_index[COMPONENT_TYPES.component][component]
402
+ if is_shared then
403
+ new_changes = new_changes or create_new_changes()
404
+ new_changes.component[component] = change
405
+ changes.component[component] = nil
406
+ end
407
+ end
408
+ for entity, entity_changes in changes.tagged do
409
+ local is_shared = shared_index[COMPONENT_TYPES.tag] and shared_index[COMPONENT_TYPES.tag][entity]
410
+ if is_shared then
411
+ new_changes = new_changes or create_new_changes()
412
+ new_changes.tagged[entity] = entity_changes
413
+ changes.tagged[entity] = nil
414
+ end
415
+ end
416
+ for entity, entity_changes in changes.pairs do
417
+ local is_shared = shared_index[COMPONENT_TYPES.pair] and shared_index[COMPONENT_TYPES.pair][entity]
418
+ if is_shared then
419
+ new_changes = new_changes or create_new_changes()
420
+ new_changes.pairs[entity] = entity_changes
421
+ changes.pairs[entity] = nil
422
+ end
423
+ end
424
+ return new_changes
425
+ end
426
+
427
+ local function merge_component_actives(current: ActiveEntity, target: ActiveEntity)
428
+ for component_type, components in current.components do
429
+ for component in components do
430
+ get_or_set_component_active(target, component, component_type)
431
+ end
432
+ end
433
+ end
434
+
435
+ local function merge_component_changes(current: EntityChanges, target: EntityChanges)
436
+ for component, change in current.component do
437
+ target.component[component] = change
438
+ end
439
+ for entity, entity_changes in current.tagged do
440
+ target.tagged[entity] = entity_changes
441
+ end
442
+ for entity, entity_changes in current.pairs do
443
+ target.pairs[entity] = entity_changes
444
+ end
445
+ end
446
+
447
+ function masking_controller.merge_component_filter(
448
+ masking: MaskingInternal,
449
+ entity: Entity,
450
+ component: Entity,
451
+ component_type: number
452
+ )
453
+ local component_lookup = get_component_index_entry(masking.lookups.components, entity, component, component_type)
454
+ local entity_lookup = masking.lookups.entities[entity]
455
+ if not entity_lookup then
456
+ utils.logerror "attempted to merge a component filter to an unactive entity"
457
+ end
458
+
459
+ if component_lookup ~= nil then
460
+ local new_storage = masking:move_component_storage(
461
+ entity,
462
+ component,
463
+ component_type,
464
+ component_lookup.storage_group,
465
+ entity_lookup.generator.bitmask
466
+ )
467
+ remove_component_index_entry(masking.lookups.components, entity, component, component_type)
468
+ remove_component_index_entry(new_storage.shared_with, entity, component, component_type)
469
+ else
470
+ get_or_set_component_active(entity_lookup.storage_group.active[entity], component, component_type)
471
+ end
472
+ end
473
+
474
+ function masking_controller.move_entity_storage(
475
+ masking: MaskingInternal,
476
+ entity: Entity,
477
+ storage: StorageGroup,
478
+ target: Bitmask
479
+ )
480
+ local target_storage = masking:get_or_create_storage_group(target)
481
+ if target_storage == storage then
482
+ return storage
483
+ end
484
+ local shared_with = storage.shared_with[entity]
485
+
486
+ local old_active = storage.active[entity]
487
+ local new_active = get_or_set_active(target_storage, entity, old_active)
488
+
489
+ new_active.is_entity_mask = true
490
+
491
+ if shared_with then
492
+ local dissected_active = dissect_component_actives(storage, entity, shared_with)
493
+ if dissected_active then
494
+ storage.active[entity] = dissected_active
495
+ else
496
+ remove_active(storage, entity)
497
+ end
498
+ else
499
+ remove_active(storage, entity)
500
+ end
501
+
502
+ if old_active ~= new_active then
503
+ merge_component_actives(old_active, new_active)
504
+ end
505
+
506
+ local old_changes = storage.changes.changed[entity]
507
+ if old_changes then
508
+ local new_changes = get_or_set_changed(target_storage, entity)
509
+
510
+ if shared_with then
511
+ local dissected_changes = dissect_component_changes(old_changes, shared_with)
512
+ if dissected_changes then
513
+ storage.changes.changed[entity] = dissected_changes
514
+ else
515
+ storage.changes.changed[entity] = nil
516
+ end
517
+ end
518
+
519
+ if old_changes ~= new_changes then
520
+ merge_component_changes(old_changes, new_changes)
521
+ end
522
+ end
523
+ return target_storage
524
+ end
525
+
526
+ function masking_controller.move_component_storage(
527
+ masking: MaskingInternal,
528
+ entity: Entity,
529
+ component: Entity,
530
+ component_type: number,
531
+ storage: StorageGroup,
532
+ target: Bitmask
533
+ )
534
+ local target_storage = masking:get_or_create_storage_group(target)
535
+ if target_storage == storage then
536
+ return storage
537
+ end
538
+
539
+ local new_active = get_or_set_active(target_storage, entity)
540
+
541
+ remove_component_active(storage, entity, component, component_type)
542
+ get_or_set_component_active(new_active, component, component_type)
543
+
544
+ set_component_index_entry(target_storage.shared_with, entity, component, component_type, true)
545
+ remove_component_index_entry(storage.shared_with, entity, component, component_type)
546
+
547
+ local changes = storage.changes.changed[entity]
548
+ if changes then
549
+ local target_changes = get_or_set_changed(target_storage, entity)
550
+ if component_type == COMPONENT_TYPES.component then
551
+ target_changes.component[component] = changes.component[component]
552
+ target_changes.removed[component] = changes.removed[component]
553
+
554
+ changes.component[component] = nil
555
+ changes.removed[component] = nil
556
+ elseif component_type == COMPONENT_TYPES.tag then
557
+ target_changes.tagged[component] = changes.tagged[component]
558
+ target_changes.removed[component] = changes.removed[component]
559
+
560
+ changes.tagged[component] = nil
561
+ changes.removed[component] = nil
562
+ elseif component_type == COMPONENT_TYPES.pair then
563
+ target_changes.pairs[component] = changes.pairs[component]
564
+ changes.pairs[component] = nil
565
+ end
566
+ end
567
+
568
+ --todo: move changes
569
+
570
+ return target_storage
571
+ end
572
+
573
+ function masking_controller.bind_component_generator(
574
+ masking: MaskingInternal,
575
+ entity: Entity,
576
+ component: Entity,
577
+ component_type: number,
578
+ base_generator: MaskGenerator
579
+ )
580
+ local entity_lookup = masking.lookups.entities[entity]
581
+ local component_lookups = masking.lookups.components[entity]
582
+ local component_lookup = component_lookups and component_lookups[component_type][component]
583
+ local generator = mask_generator.create(nil, function()
584
+ return entity_lookup.generator.bitmask:band(base_generator.bitmask)
585
+ end)
586
+
587
+ local function subscribed()
588
+ if not component_lookup then
589
+ component_lookup = component_lookups and component_lookups[component_type][component]
590
+ end
591
+
592
+ local current_storage = component_lookup.storage_group
593
+ local new_storage =
594
+ masking:move_component_storage(entity, component, component_type, current_storage, generator.bitmask)
595
+
596
+ component_lookup.storage_group = new_storage
597
+ end
598
+
599
+ generator:track(base_generator)
600
+ generator:track(masking.active_members_generator)
601
+ generator.subscribed = subscribed
602
+
603
+ return generator
604
+ end
605
+
606
+ function masking_controller.rebind_component_generators(masking: MaskingInternal, entity: Entity)
607
+ local component_lookups = masking.lookups.components[entity]
608
+ if component_lookups then
609
+ for component_type, lookups in component_lookups do
610
+ for component, lookup in lookups do
611
+ lookup.band_generator:destroy()
612
+
613
+ local binded_generator =
614
+ masking:bind_component_generator(entity, component, component_type, lookup.generator)
615
+ lookup.band_generator = binded_generator
616
+ binded_generator:run_subscribed()
617
+ end
618
+ end
619
+ end
620
+ end
621
+
622
+ function masking_controller.get_or_pospone_entity_lookup(
623
+ masking: MaskingInternal,
624
+ entity: Entity,
625
+ component: Entity,
626
+ component_type: number,
627
+ filter: MemberFilter?
628
+ ): Lookup?
629
+ local lookup = masking.lookups.entities[entity]
630
+ if lookup then
631
+ return lookup
632
+ end
633
+
634
+ if filter == nil then
635
+ return nil
636
+ end
637
+
638
+ set_component_index_entry(masking.lookups.posponed, entity, component, component_type, filter)
639
+ return nil
640
+ end
641
+
642
+ function masking_controller.register_stop_entity(masking: MaskingInternal, entity: Entity)
643
+ local lookup = masking.lookups.entities[entity]
644
+ local storage = lookup.storage_group
645
+
646
+ if storage.deletions.entities[entity] then
647
+ return
648
+ end
649
+ storage.deletions.entities[entity] = true
650
+ storage.deletions_count += 1
651
+ end
652
+
653
+ function masking_controller.register_stop_component(
654
+ masking: MaskingInternal,
655
+ entity: Entity,
656
+ component: Entity,
657
+ component_type: number
658
+ )
659
+ local storage = masking:get_component_storage_group(entity, component, component_type) :: StorageGroup
660
+
661
+ local deletions = storage.deletions.components[entity]
662
+ if deletions == nil then
663
+ deletions = create_component_indexes() :: ComponentIndex<boolean>
664
+ deletions[component_type][component] = true
665
+ storage.deletions.components[entity] = deletions
666
+ storage.deletions_count += 1
667
+ else
668
+ if deletions[component_type][component] then
669
+ return
670
+ end
671
+ deletions[component_type][component] = true
672
+ storage.deletions_count += 1
673
+ end
674
+ end
675
+ function masking_controller.unregister_stop_entity(masking: MaskingInternal, entity: Entity)
676
+ local deletion_lookup = masking.lookups.deletions.entities[entity]
677
+ if not deletion_lookup then
678
+ return
679
+ end
680
+ local storage = deletion_lookup.storage_group
681
+
682
+ if storage.deletions.entities[entity] then
683
+ storage.deletions.entities[entity] = nil
684
+ storage.deletions_count -= 1
685
+ end
686
+ end
687
+
688
+ function masking_controller.unregister_stop_component(
689
+ masking: MaskingInternal,
690
+ entity: Entity,
691
+ component: Entity,
692
+ component_type: number
693
+ )
694
+ local deletion_lookup =
695
+ get_component_index_entry(masking.lookups.deletions.components, entity, component, component_type)
696
+ if deletion_lookup == nil then
697
+ return
698
+ end
699
+ local storage = deletion_lookup.storage_group
700
+
701
+ local deletions = storage.deletions.components[entity]
702
+ if deletions then
703
+ if deletions[component_type][component] then
704
+ deletions[component_type][component] = nil
705
+ storage.deletions_count -= 1
706
+ end
707
+ end
708
+ end
709
+
710
+ function masking_controller.propagate_entity_addition(masking: MaskingInternal, entity: Entity)
711
+ local entity_lookup = masking.lookups.entities[entity]
712
+ if entity_lookup then
713
+ entity_lookup.storage_group.changes.added[entity] = true
714
+ end
715
+
716
+ local component_lookups = masking.lookups.components[entity]
717
+ if component_lookups then
718
+ for component_type, lookups in component_lookups do
719
+ for component, lookup in lookups do
720
+ lookup.storage_group.changes.added[entity] = true
721
+ end
722
+ end
723
+ end
724
+ end
725
+
726
+ function masking_controller.update_entity_filter(masking: MaskingInternal, entity: Entity, filter: MemberFilter?)
727
+ local generator: MaskGenerator
728
+ if filter then
729
+ generator = masking:create_generator_from_filter(filter)
730
+ else
731
+ generator = mask_generator.follow(masking.active_members_generator)
732
+ end
733
+ local lookup = masking.lookups.entities[entity]
734
+
735
+ local function subscribed()
736
+ if not lookup then
737
+ lookup = masking.lookups.entities[entity]
738
+ end
739
+ local current_storage = lookup.storage_group
740
+
741
+ local new_storage = masking:move_entity_storage(entity, current_storage, generator.bitmask)
742
+ lookup.storage_group = new_storage
743
+ end
744
+ generator.subscribed = subscribed
745
+ return generator
746
+ end
747
+
748
+ function masking_controller.start_entity(masking: MaskingInternal, entity: Entity, filter: MemberFilter?)
749
+ local generator = masking:update_entity_filter(entity, filter)
750
+
751
+ local storage_group = masking:get_or_create_storage_group(generator.bitmask)
752
+ local active = get_or_set_active(storage_group, entity)
753
+ active.is_entity_mask = true
754
+
755
+ local new_lookup: Lookup = {
756
+ generator = generator,
757
+ filter = filter,
758
+ storage_group = storage_group,
759
+ }
760
+ masking.lookups.entities[entity] = new_lookup
761
+
762
+ local posponed = masking.lookups.posponed[entity]
763
+ if posponed then
764
+ for component_type, components in posponed do
765
+ for component, component_filter in components do
766
+ masking:start_component(entity, component, component_type, component_filter)
767
+ end
768
+ end
769
+ masking.lookups.posponed[entity] = nil
770
+ end
771
+ end
772
+
773
+ function masking_controller.set_entity(masking: MaskingInternal, entity: Entity, filter: MemberFilter?)
774
+ local lookup = masking.lookups.entities[entity]
775
+ if not lookup then
776
+ utils.logerror "attempted to modify the filter of an unactive entity"
777
+ end
778
+ lookup.generator:destroy()
779
+
780
+ local generator = masking:update_entity_filter(entity, filter)
781
+
782
+ lookup.generator = generator
783
+ lookup.filter = filter
784
+
785
+ generator:run_subscribed()
786
+ masking:rebind_component_generators(entity)
787
+ end
788
+
789
+ function masking_controller.stop_entity(masking: MaskingInternal, entity: Entity)
790
+ local lookup = masking.lookups.entities[entity]
791
+ if not lookup then
792
+ utils.logerror "attemped to stop an unactive entity"
793
+ end
794
+
795
+ local posponed = masking.lookups.posponed[entity]
796
+
797
+ local components_lookup = masking.lookups.components[entity]
798
+ if components_lookup then
799
+ for component_type, lookups in components_lookup do
800
+ for component, component_lookup in lookups do
801
+ component_lookup.band_generator:destroy()
802
+ component_lookup.generator:destroy()
803
+ remove_component_active(component_lookup.storage_group, entity, component, component_type)
804
+
805
+ local component_storage = component_lookup.storage_group
806
+ component_storage.shared_with[entity] = nil
807
+ component_storage.changes.added[entity] = nil
808
+ component_storage.changes.changed[entity] = nil
809
+
810
+ if posponed == nil then
811
+ posponed = create_component_indexes()
812
+ masking.lookups.posponed[entity] = posponed
813
+ end
814
+ posponed[component_type][component] = component_lookup.filter :: MemberFilter
815
+ end
816
+ end
817
+ end
818
+
819
+ lookup.generator:destroy()
820
+ remove_active(lookup.storage_group, entity)
821
+ lookup.storage_group.changes.added[entity] = nil
822
+ lookup.storage_group.changes.changed[entity] = nil
823
+
824
+ masking.lookups.components[entity] = nil
825
+ masking.lookups.entities[entity] = nil
826
+ end
827
+
828
+ function masking_controller.update_component_filter(
829
+ masking: MaskingInternal,
830
+ entity: Entity,
831
+ component: Entity,
832
+ component_type: number,
833
+ filter: MemberFilter
834
+ )
835
+ local generator = masking:create_generator_from_filter(filter)
836
+ local band_generator = masking:bind_component_generator(entity, component, component_type, generator)
837
+ return generator, band_generator
838
+ end
839
+
840
+ function masking_controller.start_component(
841
+ masking: MaskingInternal,
842
+ entity: Entity,
843
+ component: Entity,
844
+ component_type: number,
845
+ filter: MemberFilter?
846
+ )
847
+ local lookup = masking:get_or_pospone_entity_lookup(entity, component, component_type, filter)
848
+ if lookup == nil then
849
+ return
850
+ end
851
+
852
+ if filter == nil then
853
+ get_or_set_component_active(lookup.storage_group.active[entity], component, component_type)
854
+ return
855
+ end
856
+
857
+ local generator, band_generator = masking:update_component_filter(entity, component, component_type, filter)
858
+ local storage_group = masking:get_or_create_storage_group(band_generator.bitmask)
859
+ local active = get_or_set_active(storage_group, entity)
860
+
861
+ get_or_set_component_active(active, component, component_type)
862
+ set_component_index_entry(storage_group.shared_with, entity, component, component_type, true)
863
+
864
+ set_component_index_entry(masking.lookups.components, entity, component, component_type, {
865
+ generator = generator,
866
+ band_generator = band_generator,
867
+ filter = filter,
868
+ storage_group = storage_group,
869
+ })
870
+ end
871
+
872
+ function masking_controller.set_component(
873
+ masking: MaskingInternal,
874
+ entity: Entity,
875
+ component: Entity,
876
+ component_type: number,
877
+ filter: MemberFilter?
878
+ )
879
+ local lookup = masking:get_or_pospone_entity_lookup(entity, component, component_type, filter)
880
+ if lookup == nil then
881
+ return
882
+ end
883
+ local component_lookup = get_component_index_entry(masking.lookups.components, entity, component, component_type)
884
+
885
+ if filter == nil then
886
+ if component_lookup ~= nil then
887
+ component_lookup.band_generator:destroy()
888
+ component_lookup.generator:destroy()
889
+
890
+ masking:merge_component_filter(entity, component, component_type)
891
+ end
892
+ return
893
+ end
894
+
895
+ local generator, band_generator = masking:update_component_filter(entity, component, component_type, filter)
896
+
897
+ if component_lookup ~= nil then
898
+ component_lookup.band_generator:destroy()
899
+ component_lookup.generator:destroy()
900
+
901
+ component_lookup.band_generator = band_generator
902
+ component_lookup.generator = generator
903
+ component_lookup.filter = filter
904
+
905
+ band_generator:run_subscribed()
906
+ else
907
+ local desired_storage = masking:get_or_create_storage_group(band_generator.bitmask)
908
+ local new_storage: StorageGroup
909
+
910
+ if desired_storage and desired_storage == lookup.storage_group then
911
+ new_storage = lookup.storage_group
912
+ set_component_index_entry(desired_storage.shared_with, entity, component, component_type, true)
913
+ else
914
+ new_storage = masking:move_component_storage(
915
+ entity,
916
+ component,
917
+ component_type,
918
+ lookup.storage_group,
919
+ band_generator.bitmask
920
+ )
921
+ end
922
+
923
+ set_component_index_entry(masking.lookups.components, entity, component, component_type, {
924
+ generator = generator,
925
+ band_generator = band_generator,
926
+ filter = filter,
927
+ storage_group = new_storage,
928
+ })
929
+ end
930
+ end
931
+
932
+ function masking_controller.stop_component(
933
+ masking: MaskingInternal,
934
+ entity: Entity,
935
+ component: Entity,
936
+ component_type: number
937
+ )
938
+ local lookup = get_component_index_entry(masking.lookups.components, entity, component, component_type)
939
+ if lookup ~= nil then
940
+ lookup.band_generator:destroy()
941
+ lookup.generator:destroy()
942
+
943
+ remove_component_active(lookup.storage_group, entity, component, component_type)
944
+ remove_component_index_entry(lookup.storage_group.shared_with, entity, component, component_type)
945
+ remove_component_index_entry(masking.lookups.components, entity, component, component_type)
946
+ else
947
+ local entity_lookup = masking.lookups.entities[entity]
948
+ if entity_lookup then
949
+ remove_component_active(entity_lookup.storage_group, entity, component, component_type)
950
+ end
951
+ end
952
+ end
953
+
954
+ function masking_controller.get_component_storage_group(
955
+ masking: MaskingInternal,
956
+ entity: Entity,
957
+ component: Entity,
958
+ component_type: number
959
+ ): StorageGroup
960
+ local component_lookup = masking_controller.get_component_index_entry(
961
+ masking.lookups.components,
962
+ entity,
963
+ component,
964
+ COMPONENT_TYPES.component
965
+ )
966
+ if component_lookup ~= nil then
967
+ return component_lookup.storage_group
968
+ else
969
+ local entity_lookup = masking.lookups.entities[entity]
970
+ if not entity_lookup then
971
+ utils.logerror "attempted to use an entity that is not active"
972
+ end
973
+ return entity_lookup.storage_group
974
+ end
975
+ end
976
+
977
+ function masking_controller.allocate_entity_addition(
978
+ masking: MaskingInternal,
979
+ entity: Entity,
980
+ component: Entity,
981
+ component_type: number
982
+ )
983
+ local lookup = get_component_index_entry(masking.lookups.components, entity, component, component_type)
984
+ if lookup ~= nil then
985
+ lookup.storage_group.changes.added[entity] = true
986
+ end
987
+ end
988
+
989
+ function masking_controller.allocate_component_change(
990
+ masking: MaskingInternal,
991
+ entity: Entity,
992
+ component: Entity,
993
+ value: any
994
+ )
995
+ local storage = masking:get_component_storage_group(entity, component, COMPONENT_TYPES.component)
996
+ local changes = get_or_set_changes(storage, entity)
997
+ changes.component[component] = value
998
+ end
999
+
1000
+ function masking_controller.allocate_tag_addition(masking: MaskingInternal, entity: Entity, tag: Entity)
1001
+ local storage = masking:get_component_storage_group(entity, tag, COMPONENT_TYPES.tag)
1002
+ local changes = get_or_set_changes(storage, entity)
1003
+ changes.tagged[tag] = true
1004
+ end
1005
+
1006
+ function masking_controller.allocate_pair_addition(
1007
+ masking: MaskingInternal,
1008
+ entity: Entity,
1009
+ relation: Entity,
1010
+ target: Entity
1011
+ )
1012
+ local storage = masking:get_component_storage_group(entity, relation, COMPONENT_TYPES.pair)
1013
+ local changes = get_or_set_changes(storage, entity)
1014
+ local targets = changes.pairs[relation]
1015
+ if targets == nil then
1016
+ targets = {}
1017
+ changes.pairs[relation] = targets
1018
+ end
1019
+ targets[target] = true
1020
+ end
1021
+
1022
+ function masking_controller.allocate_component_removal(masking: MaskingInternal, entity: Entity, component: Entity)
1023
+ local storage = masking:get_component_storage_group(entity, component, COMPONENT_TYPES.component)
1024
+ local changes = get_or_set_changes(storage, entity)
1025
+
1026
+ if changes.component[component] then
1027
+ changes.component[component] = nil
1028
+ end
1029
+ changes.removed[component] = true
1030
+ end
1031
+
1032
+ function masking_controller.allocate_tag_removal(masking: MaskingInternal, entity: Entity, tag: Entity)
1033
+ local storage = masking:get_component_storage_group(entity, tag, COMPONENT_TYPES.tag)
1034
+ local changes = get_or_set_changes(storage, entity)
1035
+
1036
+ if changes.tagged[tag] then
1037
+ changes.tagged[tag] = nil
1038
+ end
1039
+ changes.removed[tag] = true
1040
+ end
1041
+
1042
+ function masking_controller.allocate_pair_removal(
1043
+ masking: MaskingInternal,
1044
+ entity: Entity,
1045
+ relation: Entity,
1046
+ target: Entity
1047
+ )
1048
+ local storage = masking:get_component_storage_group(entity, relation, COMPONENT_TYPES.pair)
1049
+ local changes = get_or_set_changes(storage, entity)
1050
+ local targets = changes.pairs[relation]
1051
+ if targets == nil then
1052
+ targets = {}
1053
+ changes.pairs[relation] = targets
1054
+ end
1055
+ targets[target] = false
1056
+ end
1057
+
1058
+ function masking_controller.compact_members()
1059
+ -- TODO: implement
1060
+ end
1061
+
1062
+ function masking_controller.create_generator_from_filter(masking: MaskingInternal, filter: MemberFilter): MaskGenerator
1063
+ local is_include = is_include_filter(filter)
1064
+ if is_include then
1065
+ return masking:create_include_generator(filter) :: MaskGenerator
1066
+ else
1067
+ return masking:create_exclude_generator(filter) :: MaskGenerator
1068
+ end
1069
+ end
1070
+
1071
+ function masking_controller.create_exclude_generator(masking: MaskingInternal, exclude: MemberFilter): MaskGenerator
1072
+ local bitmask = utils.bitmask.from_set(masking.member_indexes, exclude, masking.member_count)
1073
+ local active = masking.active_members_generator
1074
+
1075
+ local generator = mask_generator.create(nil, function()
1076
+ return active.bitmask:band(bitmask:bnot())
1077
+ end)
1078
+ generator:track(active)
1079
+
1080
+ return generator
1081
+ end
1082
+
1083
+ -- TODO: check for players existing and register them if they don't
1084
+ function masking_controller.create_include_generator(masking: MaskingInternal, filter: MemberFilter): MaskGenerator
1085
+ local bitmask = utils.bitmask.from_set(masking.member_indexes, filter, masking.member_count)
1086
+ local active = masking.active_members_generator
1087
+
1088
+ local generator = mask_generator.create(nil, function()
1089
+ return bitmask:band(active.bitmask)
1090
+ end)
1091
+ generator:track(active)
1092
+
1093
+ return generator
1094
+ end
1095
+
1096
+ function masking_controller.create_all_members_generator(masking: MaskingInternal): MaskGenerator
1097
+ local filter: MemberFilter = {}
1098
+ for member in masking.member_indexes do
1099
+ filter[member] = true
1100
+ end
1101
+ local bitmask = utils.bitmask.from_set(masking.member_indexes, filter, masking.member_count)
1102
+ return mask_generator.create(bitmask)
1103
+ end
1104
+ function masking_controller.create_unactive_generator(masking: MaskingController): MaskGenerator
1105
+ local bitmask = utils.bitmask.from_set(masking.member_indexes, masking.unreplicated, masking.member_count)
1106
+ return mask_generator.create(bitmask)
1107
+ end
1108
+ function masking_controller.create_active_generator(masking: MaskingController): MaskGenerator
1109
+ local all = masking.all_members_generator
1110
+ local unactive = masking.unactive_members_generator
1111
+
1112
+ local new_generator = mask_generator.create(nil, function()
1113
+ return all.bitmask:band(unactive.bitmask:bnot())
1114
+ end)
1115
+ new_generator:track(all)
1116
+ new_generator:track(unactive)
1117
+
1118
+ return new_generator
1119
+ end
1120
+
1121
+ function masking_controller.register_member(masking: MaskingInternal, member: Member)
1122
+ if masking.member_indexes[member] then
1123
+ return
1124
+ end
1125
+
1126
+ local current = masking.member_count
1127
+ masking.member_indexes[member] = current
1128
+ masking.member_count += 1
1129
+
1130
+ masking.unreplicated[member] = true
1131
+ masking.unactive_members_generator.bitmask:set(current)
1132
+ masking.all_members_generator.bitmask:set(current)
1133
+ masking.active_members_generator:compute()
1134
+ end
1135
+
1136
+ function masking_controller.unregister_member(masking: MaskingInternal, member: Member)
1137
+ local index = masking.member_indexes[member]
1138
+ if index == nil then
1139
+ return
1140
+ end
1141
+ masking.member_indexes[member] = nil
1142
+ masking.member_count -= 1
1143
+ masking.all_members_generator.bitmask:clear(index)
1144
+ masking.active_members_generator:compute()
1145
+ end
1146
+
1147
+ function masking_controller.activate_member(masking: MaskingInternal, member: Member)
1148
+ local index = masking.member_indexes[member]
1149
+ if index == nil then
1150
+ masking:register_member(member)
1151
+ index = masking.member_indexes[member]
1152
+ end
1153
+
1154
+ masking.unreplicated[member] = nil
1155
+ masking.unactive_members_generator.bitmask:clear(index)
1156
+ masking.active_members_generator:compute()
1157
+ end
1158
+
1159
+ function masking_controller.member_is_active(masking: MaskingInternal, member: Member): boolean
1160
+ local index = masking.member_indexes[member]
1161
+ if index == nil then
1162
+ return false
1163
+ end
1164
+
1165
+ return masking.active_members_generator.bitmask:get(index)
1166
+ end
1167
+
1168
+ local function create(): MaskingController
1169
+ local self = {} :: MaskingInternal
1170
+
1171
+ self.member_indexes = {}
1172
+ self.member_count = 0
1173
+ self.storages = {}
1174
+ self.lookups = {
1175
+ entities = {},
1176
+ components = {},
1177
+ deletions = {
1178
+ entities = {},
1179
+ components = {},
1180
+ },
1181
+ posponed = {},
1182
+ }
1183
+ self.unreplicated = {}
1184
+ self.all_members_generator = masking_controller.create_all_members_generator(self)
1185
+ self.unactive_members_generator = masking_controller.create_unactive_generator(self)
1186
+ self.active_members_generator = masking_controller.create_active_generator(self)
1187
+
1188
+ return setmetatable(self, masking_controller) :: any
1189
+ end
1190
+
1191
+ return { create = create, masking_controller = masking_controller, COMPONENT_TYPES = COMPONENT_TYPES }