@rbxts/replecs 0.4.0-rc.3 → 0.4.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,196 +1,366 @@
1
+ --!optimize 2
1
2
  local types = require (script.Parent.Parent:WaitForChild('types'))
2
3
  local registry = require (script.Parent:WaitForChild('registry'))
3
4
  local bitmasks = require (script.Parent:WaitForChild('bitmasks'))
4
5
  local utils = require (script.Parent:WaitForChild('utils'))
5
- local common = require (script.Parent.Parent:WaitForChild('common'))
6
6
 
7
7
  type Bitmask = buffer
8
+ type Player = any
8
9
 
9
10
  type FilterSimple = types.FilterSimple
11
+ type FilterPredicator = types.FilterPredicator
10
12
 
11
13
  type Set<T> = { [T]: boolean }
12
- type Map<K, V> = { [K]: V }
13
14
  type Array<T> = { T }
14
-
15
- type Listener = (Bitmask) -> ()
16
-
17
- type ListenerConnections = {
18
- listeners: { Listener },
19
- listeners_indexes: { [Listener]: number },
15
+ type Registry = registry.Registry
16
+
17
+ local KINDS = {
18
+ source = 1,
19
+ predicate = 2,
20
+ intersect = 3,
21
+ union = 4,
22
+ exclude = 5,
20
23
  }
21
24
 
22
- export type FilterMode = number
23
-
24
- export type FilterEntry = {
25
- mode: FilterMode,
26
- filter: FilterSimple,
25
+ export type FilterNodeClass = {
26
+ kind: number,
27
+ registry: Registry?,
28
+
29
+ inputs: Array<FilterNode>,
30
+ input_versions: Array<number>,
31
+ dependents: Set<FilterNode>,
32
+
33
+ value: Bitmask?,
34
+ scratch: Bitmask?,
35
+ version: number,
36
+ dirty: boolean,
37
+ resolved_epoch: number,
38
+ resolved_cycle: number,
39
+
40
+ -- source only
41
+ member_set: Set<Player>?,
42
+ inverted: boolean?,
43
+ predicator: FilterPredicator?,
27
44
  }
28
45
 
29
- export type FilterGroupClass = {
30
- registry: registry.Registry,
31
- parent: FilterGroup?,
46
+ local filter_node = {}
47
+ filter_node.__index = filter_node
32
48
 
33
- derived: Set<FilterGroup>,
34
- assigned_filter: FilterEntry?,
35
- connected_listeners: ListenerConnections,
36
- }
49
+ export type FilterNode = FilterNodeClass & typeof(filter_node)
50
+ export type FilterOperand = FilterNode | FilterSimple
37
51
 
38
- local FILTER_MODES = {
39
- extend = 1,
40
- exclude = 2,
41
- include = 3,
42
- }
52
+ function filter_node.is_filter_node(value: any): boolean
53
+ return typeof(value) == "table" and getmetatable(value) == filter_node
54
+ end
43
55
 
44
- local filter_group = {}
45
- filter_group.__index = filter_group
56
+ function filter_node.bind(node: FilterNode, reg: Registry)
57
+ if node.registry == reg then
58
+ return
59
+ end
60
+ if node.registry then
61
+ error "filter is already bound to a different server"
62
+ end
46
63
 
47
- export type FilterGroup = FilterGroupClass & typeof(filter_group)
64
+ node.registry = reg
65
+ node.value = buffer.create(reg.bitmask_size)
66
+ node.scratch = buffer.create(reg.bitmask_size)
48
67
 
49
- function filter_group.is_filter_group(value: any): boolean
50
- return typeof(value) == "table" and getmetatable(value) == filter_group
68
+ for _, input in node.inputs do
69
+ filter_node.bind(input, reg)
70
+ end
51
71
  end
52
72
 
53
- local function fire_listeners(self: FilterGroup, bitmask: Bitmask)
54
- for _, listener in self.connected_listeners.listeners do
55
- listener(bitmask)
73
+ local function create_node(kind: number, reg: Registry?): FilterNode
74
+ local self: FilterNodeClass = {
75
+ kind = kind,
76
+ registry = nil,
77
+
78
+ inputs = {},
79
+ input_versions = {},
80
+ dependents = setmetatable({}, { __mode = "k" }) :: any,
81
+
82
+ value = nil,
83
+ scratch = nil,
84
+ version = 0,
85
+ dirty = true,
86
+ resolved_epoch = -1,
87
+ resolved_cycle = -1,
88
+
89
+ member_set = nil,
90
+ inverted = nil,
91
+ predicator = nil,
92
+ }
93
+ setmetatable(self, filter_node)
94
+
95
+ local node = self :: any
96
+ if reg then
97
+ filter_node.bind(node, reg)
56
98
  end
99
+ return node
57
100
  end
58
101
 
59
- function filter_group.connect(self: FilterGroup, listener: Listener)
60
- local connected = self.connected_listeners
61
- local index = #connected.listeners + 1
62
- connected.listeners[index] = listener
63
- connected.listeners_indexes[listener] = index
102
+ local function mark_dirty(node: FilterNode)
103
+ if node.dirty then
104
+ return
105
+ end
106
+ node.dirty = true
107
+ for dependent in node.dependents do
108
+ mark_dirty(dependent)
109
+ end
64
110
  end
65
111
 
66
- function filter_group.disconnect(self: FilterGroup, listener: Listener)
67
- local connected = self.connected_listeners
68
- local index = connected.listeners_indexes[listener]
69
- if not index then
70
- return
112
+ local function assert_source(node: FilterNode, method: string)
113
+ if node.kind ~= KINDS.source then
114
+ error(`{method}() is only available on source filters, derived filters are read only`)
71
115
  end
116
+ end
117
+
72
118
 
73
- local last_listener = table.remove(connected.listeners)
74
- if last_listener and last_listener ~= listener then
75
- connected.listeners[index] = last_listener
76
- connected.listeners_indexes[last_listener] = index
119
+ local function resolve_source(node: FilterNode, reg: Registry, scratch: Bitmask)
120
+ bitmasks.clear(scratch)
121
+ for member in node.member_set :: Set<Player> do
122
+ local index = registry.resolve_member(reg, member)
123
+ if index then
124
+ bitmasks.set(scratch, index)
125
+ end
126
+ end
127
+ if node.inverted then
128
+ bitmasks.exclude(reg.registered_bitmask, scratch, scratch)
77
129
  end
78
- connected.listeners_indexes[listener] = nil
79
130
  end
80
131
 
81
- local function create_filter_bitmask(self: FilterGroup, filter: FilterSimple): Bitmask
82
- if typeof(filter) == "table" then
83
- return self.registry:create_bitmask_from_set(filter)
84
- else
85
- return self.registry:create_bitmask_from_member(filter)
132
+
133
+ local function resolve_predicate(node: FilterNode, reg: Registry, scratch: Bitmask)
134
+ bitmasks.clear(scratch)
135
+ local predicator = node.predicator :: FilterPredicator
136
+ for member, index in reg.client_indexes do
137
+ if predicator(member) then
138
+ bitmasks.set(scratch, index)
139
+ end
86
140
  end
87
141
  end
88
142
 
89
- -- the filter bitmask is resolved from the raw filter here, at calculation
90
- -- time, so clients registered after the filter was assigned still resolve
91
- function filter_group.apply_filter(self: FilterGroup, bitmask: Bitmask)
92
- local filter = self.assigned_filter
93
- if not filter then
94
- return
143
+
144
+ local function resolve_computed(node: FilterNode, scratch: Bitmask): boolean
145
+ local inputs = node.inputs
146
+ local input_versions = node.input_versions
147
+
148
+ local changed = false
149
+ for i, input in inputs do
150
+ filter_node.resolve(input)
151
+ if input.version ~= input_versions[i] then
152
+ changed = true
153
+ end
154
+ end
155
+ if not changed then
156
+ return false
95
157
  end
96
158
 
97
- local filter_bitmask = create_filter_bitmask(self, filter.filter)
159
+ for i, input in inputs do
160
+ input_versions[i] = input.version
161
+ end
98
162
 
99
- if filter.mode == FILTER_MODES.extend then
100
- bitmasks.bor(bitmask, filter_bitmask, bitmask)
101
- elseif filter.mode == FILTER_MODES.exclude then
102
- bitmasks.exclude(bitmask, filter_bitmask, bitmask)
103
- elseif filter.mode == FILTER_MODES.include then
104
- bitmasks.band(bitmask, filter_bitmask, bitmask)
163
+ bitmasks.load(inputs[1].value :: Bitmask, scratch)
164
+ local kind = node.kind
165
+ local op: (Bitmask, Bitmask, Bitmask) -> () = if kind == KINDS.intersect
166
+ then bitmasks.band
167
+ elseif kind == KINDS.union then bitmasks.bor
168
+ else bitmasks.exclude
169
+ for i = 2, #inputs do
170
+ op(scratch, inputs[i].value :: Bitmask, scratch)
105
171
  end
172
+ return true
106
173
  end
107
174
 
108
- function filter_group.calculate_up(self: FilterGroup): Bitmask
109
- local parent = self.parent
110
- local bitmask: Bitmask
111
- if parent then
112
- bitmask = filter_group.calculate_up(parent)
113
- else
114
- bitmask = bitmasks.create_filled(self.registry.client_next_index)
175
+ function filter_node.resolve(node: FilterNode): Bitmask
176
+ local reg = node.registry
177
+ if not reg then
178
+ error "filter is not bound to a server, pass it to a set_* call first"
179
+ end
180
+ local value = node.value :: Bitmask
181
+ local kind = node.kind
182
+
183
+ if kind == KINDS.predicate then
184
+ if node.resolved_cycle == reg.cycle then
185
+ return value
186
+ end
187
+ node.resolved_cycle = reg.cycle
188
+ elseif not node.dirty and node.resolved_epoch == reg.epoch then
189
+ return value
190
+ end
191
+ node.dirty = false
192
+ node.resolved_epoch = reg.epoch
193
+
194
+ local scratch = node.scratch :: Bitmask
195
+ if kind == KINDS.source then
196
+ resolve_source(node, reg, scratch)
197
+ elseif kind == KINDS.predicate then
198
+ resolve_predicate(node, reg, scratch)
199
+ elseif not resolve_computed(node, scratch) then
200
+ return value
115
201
  end
116
202
 
117
- filter_group.apply_filter(self, bitmask)
118
- return bitmask
203
+ if bitmasks.equals(scratch, value) then
204
+ return value
205
+ end
206
+ node.value = scratch
207
+ node.scratch = value
208
+ node.version += 1
209
+ return scratch
119
210
  end
120
211
 
121
- function filter_group.calculate_down(self: FilterGroup, parent_bitmask: Bitmask)
122
- local bitmask = bitmasks.clone(parent_bitmask, bitmasks.get_buffer_size(self.registry.client_next_index))
123
- filter_group.apply_filter(self, bitmask)
124
-
125
- fire_listeners(self, bitmask)
212
+ function filter_node.set(self: FilterNode, filter: FilterSimple)
213
+ assert_source(self, "set")
126
214
 
127
- for derived in self.derived do
128
- filter_group.calculate_down(derived, bitmask)
215
+ local members: Set<Player> = {}
216
+ local inverted = false
217
+ if typeof(filter) == "table" then
218
+ inverted = not utils.is_include_filter(filter)
219
+ for member, allow in filter :: Set<Player> do
220
+ if allow == inverted then
221
+ error "a filter set cannot mix included and excluded players"
222
+ end
223
+ members[member] = true
224
+ end
225
+ else
226
+ members[filter] = true
129
227
  end
228
+
229
+ self.member_set = members
230
+ self.inverted = inverted
231
+ mark_dirty(self)
130
232
  end
131
233
 
132
- local function recalculate_filter_group(group: FilterGroup)
133
- local parent = group.parent
234
+ function filter_node.add(self: FilterNode, player: Player)
235
+ assert_source(self, "add")
236
+ local members = self.member_set :: Set<Player>
237
+ if members[player] then
238
+ return
239
+ end
240
+ members[player] = true
241
+ mark_dirty(self)
242
+ end
134
243
 
135
- local parent_bitmask: Bitmask
136
- if parent then
137
- parent_bitmask = filter_group.calculate_up(parent)
138
- else
139
- parent_bitmask = bitmasks.create_filled(group.registry.client_next_index)
244
+ function filter_node.remove(self: FilterNode, player: Player)
245
+ assert_source(self, "remove")
246
+ local members = self.member_set :: Set<Player>
247
+ if not members[player] then
248
+ return
140
249
  end
250
+ members[player] = nil
251
+ mark_dirty(self)
252
+ end
141
253
 
142
- filter_group.calculate_down(group, parent_bitmask)
254
+ function filter_node.clear(self: FilterNode)
255
+ assert_source(self, "clear")
256
+ local members = self.member_set :: Set<Player>
257
+ if next(members) == nil then
258
+ return
259
+ end
260
+ table.clear(members)
261
+ mark_dirty(self)
143
262
  end
144
263
 
145
- function filter_group.extend(self: FilterGroup, filter: FilterSimple)
146
- if not utils.get_used_filter_type(filter) then
147
- common.log_error "extend() only accepts include filters"
264
+ function filter_node.create_source(reg: Registry?, initial: FilterSimple?): FilterNode
265
+ local node = create_node(KINDS.source, reg)
266
+ node.member_set = {}
267
+ node.inverted = false
268
+ if initial ~= nil then
269
+ filter_node.set(node, initial)
148
270
  end
271
+ return node
272
+ end
149
273
 
150
- self.assigned_filter = {
151
- mode = FILTER_MODES.extend,
152
- filter = filter,
153
- }
154
- recalculate_filter_group(self)
274
+ function filter_node.create_everyone(reg: Registry?): FilterNode
275
+ local node = create_node(KINDS.source, reg)
276
+ node.member_set = {}
277
+ node.inverted = true
278
+ return node
155
279
  end
156
280
 
157
- function filter_group.filter(self: FilterGroup, filter: FilterSimple)
158
- local mode = FILTER_MODES.exclude
281
+ function filter_node.create_predicate(reg: Registry?, predicator: FilterPredicator): FilterNode
282
+ local node = create_node(KINDS.predicate, reg)
283
+ node.predicator = predicator
284
+ return node
285
+ end
159
286
 
160
- if utils.get_used_filter_type(filter) then
161
- mode = FILTER_MODES.include
287
+ local function to_node(operand: FilterOperand): FilterNode
288
+ if filter_node.is_filter_node(operand) then
289
+ return operand :: FilterNode
162
290
  end
291
+ return filter_node.create_source(nil, operand :: FilterSimple)
292
+ end
163
293
 
164
- self.assigned_filter = {
165
- mode = mode,
166
- filter = filter,
167
- }
168
- recalculate_filter_group(self)
294
+ local function create_computed(kind: number, base: FilterNode, operands: Array<FilterOperand>): FilterNode
295
+ local node = create_node(kind, nil)
296
+ local inputs = node.inputs
297
+ local input_versions = node.input_versions
298
+
299
+ inputs[1] = base
300
+ input_versions[1] = -1
301
+ local reg = base.registry
302
+
303
+ for i, operand in operands do
304
+ local input = to_node(operand)
305
+ inputs[i + 1] = input
306
+ input_versions[i + 1] = -1
307
+ reg = reg or input.registry
308
+ end
309
+
310
+ for _, input in inputs do
311
+ input.dependents[node] = true
312
+ end
313
+ if reg then
314
+ filter_node.bind(node, reg)
315
+ end
316
+ return node
169
317
  end
170
318
 
171
- function filter_group.create(client_registry: registry.Registry): FilterGroup
172
- local self: FilterGroupClass = {
173
- registry = client_registry,
319
+ function filter_node.filter(self: FilterNode, ...: FilterOperand): FilterNode
320
+ return create_computed(KINDS.intersect, self, { ... })
321
+ end
174
322
 
175
- parent = nil,
176
- connected_listeners = {
177
- listeners = {},
178
- listeners_indexes = {},
179
- },
180
- derived = setmetatable({}, { __mode = "k" }) :: any,
181
- assigned_filter = nil,
182
- }
323
+ function filter_node.extend(self: FilterNode, ...: FilterOperand): FilterNode
324
+ return create_computed(KINDS.union, self, { ... })
325
+ end
183
326
 
184
- setmetatable(self, filter_group)
185
- return self :: any
327
+ function filter_node.exclude(self: FilterNode, ...: FilterOperand): FilterNode
328
+ return create_computed(KINDS.exclude, self, { ... })
186
329
  end
187
330
 
188
- function filter_group.derive(self: FilterGroup): FilterGroup
189
- local derived = filter_group.create(self.registry)
190
- derived.parent = self
331
+ function filter_node.members(self: FilterNode): Set<Player>
332
+ local value = filter_node.resolve(self)
333
+ local index_clients = (self.registry :: Registry).index_clients
334
+
335
+ local members: Set<Player> = {}
336
+ bitmasks.iter_set(value, function(index)
337
+ local client = index_clients[index]
338
+ if client ~= nil then
339
+ members[client] = true
340
+ end
341
+ end)
342
+ return members
343
+ end
191
344
 
192
- self.derived[derived] = true
193
- return derived
345
+ local function lookup_index(reg: Registry, player: Player): number?
346
+ local index = reg.client_indexes[player]
347
+ if index then
348
+ return index
349
+ end
350
+ local aliased = reg.client_aliases[player]
351
+ if aliased == nil then
352
+ return nil
353
+ end
354
+ return reg.client_indexes[aliased]
355
+ end
356
+
357
+ function filter_node.contains(self: FilterNode, player: Player): boolean
358
+ local value = filter_node.resolve(self)
359
+ local index = lookup_index(self.registry :: Registry, player)
360
+ if index == nil then
361
+ return false
362
+ end
363
+ return bitmasks.test(value, index)
194
364
  end
195
365
 
196
- return filter_group
366
+ return filter_node