@rbxts/replecs 0.4.0-rc.2.ts.1 → 0.4.0-rc.4

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