@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.
- package/out/index.d.ts +3 -0
- package/out/init.luau +5 -0
- package/out/jecs.d.ts +1 -0
- package/out/jecs.luau +7 -0
- package/out/replecs/client.luau +354 -0
- package/out/replecs/common.luau +49 -0
- package/out/replecs/index.d.ts +96 -0
- package/out/replecs/init.luau +83 -0
- package/out/replecs/masking/init.luau +1191 -0
- package/out/replecs/masking/mask_generator.luau +97 -0
- package/out/replecs/server.luau +918 -0
- package/out/replecs/utils.luau +624 -0
- package/package.json +46 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
--!native
|
|
2
|
+
--!optimize 2
|
|
3
|
+
|
|
4
|
+
local jecs = require(script.Parent.Parent.jecs)
|
|
5
|
+
local common = require(script.Parent.common)
|
|
6
|
+
|
|
7
|
+
type World = jecs.World
|
|
8
|
+
type Entity<T = any> = jecs.Entity<T>
|
|
9
|
+
|
|
10
|
+
export type Cursor = {
|
|
11
|
+
offset: number,
|
|
12
|
+
buffer: buffer,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
local ECS_NAME = jecs.Name
|
|
16
|
+
local CHILD_OF = jecs.ChildOf
|
|
17
|
+
local WILDCARD = jecs.Wildcard
|
|
18
|
+
|
|
19
|
+
function PAIR(first: Entity, second: Entity): Entity
|
|
20
|
+
return jecs.pair(first, second)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
local utils = {}
|
|
24
|
+
|
|
25
|
+
local cursor = {}
|
|
26
|
+
|
|
27
|
+
function cursor.new(): Cursor
|
|
28
|
+
return {
|
|
29
|
+
buffer = buffer.create(100),
|
|
30
|
+
offset = 0,
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
function cursor.from(buf: buffer): Cursor
|
|
35
|
+
return {
|
|
36
|
+
buffer = buf,
|
|
37
|
+
offset = buffer.len(buf),
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
function cursor.tryalloc(cursor: Cursor, bytes: number)
|
|
42
|
+
local buf = cursor.buffer
|
|
43
|
+
local offset = cursor.offset
|
|
44
|
+
local len = buffer.len(buf)
|
|
45
|
+
|
|
46
|
+
if len < offset + bytes then
|
|
47
|
+
local exponent = math.ceil(math.log((bytes + offset) / len, 1.5))
|
|
48
|
+
local new = buffer.create(len * 1.5 ^ exponent)
|
|
49
|
+
buffer.copy(new, 0, buf, 0)
|
|
50
|
+
cursor.buffer = new
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
function cursor.write_buffer(c: Cursor, buf: buffer)
|
|
55
|
+
cursor.tryalloc(c, buffer.len(buf))
|
|
56
|
+
buffer.copy(c.buffer, c.offset, buf, 0, buffer.len(buf))
|
|
57
|
+
c.offset += buffer.len(buf)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
function cursor.writeu8(c: Cursor, value: number)
|
|
61
|
+
cursor.tryalloc(c, 1)
|
|
62
|
+
buffer.writeu8(c.buffer, c.offset, value)
|
|
63
|
+
c.offset += 1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
function cursor.writeu16(c: Cursor, value: number)
|
|
67
|
+
cursor.tryalloc(c, 2)
|
|
68
|
+
buffer.writeu16(c.buffer, c.offset, value)
|
|
69
|
+
c.offset += 2
|
|
70
|
+
end
|
|
71
|
+
function cursor.writeu24(c: Cursor, value: number)
|
|
72
|
+
cursor.tryalloc(c, 3)
|
|
73
|
+
buffer.writeu8(c.buffer, c.offset, value)
|
|
74
|
+
buffer.writeu16(c.buffer, c.offset + 1, value // 256)
|
|
75
|
+
c.offset += 3
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
function cursor.writeu32(c: Cursor, value: number)
|
|
79
|
+
cursor.tryalloc(c, 4)
|
|
80
|
+
buffer.writeu32(c.buffer, c.offset, value :: any)
|
|
81
|
+
c.offset += 4
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
function cursor.writei8(c: Cursor, value: number)
|
|
85
|
+
cursor.tryalloc(c, 1)
|
|
86
|
+
buffer.writei8(c.buffer, c.offset, value)
|
|
87
|
+
c.offset += 1
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
function cursor.writei16(c: Cursor, value: number)
|
|
91
|
+
cursor.tryalloc(c, 2)
|
|
92
|
+
buffer.writei16(c.buffer, c.offset, value)
|
|
93
|
+
c.offset += 2
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
function cursor.writei32(c: Cursor, value: number)
|
|
97
|
+
cursor.tryalloc(c, 4)
|
|
98
|
+
buffer.writeu32(c.buffer, c.offset, value :: any)
|
|
99
|
+
c.offset += 4
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
function cursor.read_buffer(c: Cursor, len: number)
|
|
103
|
+
local retrieved = buffer.create(len)
|
|
104
|
+
buffer.copy(retrieved, 0, c.buffer, c.offset - len, len)
|
|
105
|
+
c.offset -= len
|
|
106
|
+
return retrieved
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
function cursor.readu8(c: Cursor): number
|
|
110
|
+
c.offset -= 1
|
|
111
|
+
return buffer.readu8(c.buffer, c.offset)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
function cursor.readu16(c: Cursor): number
|
|
115
|
+
c.offset -= 2
|
|
116
|
+
return buffer.readu16(c.buffer, c.offset)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
function cursor.readu24(c: Cursor): number
|
|
120
|
+
c.offset -= 3
|
|
121
|
+
local one = buffer.readu8(c.buffer, c.offset)
|
|
122
|
+
local two = buffer.readu16(c.buffer, c.offset + 1)
|
|
123
|
+
return one + two * 256
|
|
124
|
+
end
|
|
125
|
+
function cursor.readu32(c: Cursor): number
|
|
126
|
+
c.offset -= 4
|
|
127
|
+
return buffer.readu32(c.buffer, c.offset)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
function cursor.readi8(c: Cursor): number
|
|
131
|
+
c.offset -= 1
|
|
132
|
+
return buffer.readi8(c.buffer, c.offset)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
function cursor.readi16(c: Cursor): number
|
|
136
|
+
c.offset -= 2
|
|
137
|
+
return buffer.readi16(c.buffer, c.offset)
|
|
138
|
+
end
|
|
139
|
+
function cursor.readi32(c: Cursor): number
|
|
140
|
+
c.offset -= 4
|
|
141
|
+
return buffer.readi32(c.buffer, c.offset)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
function cursor.write_vlq(c: Cursor, value: number)
|
|
145
|
+
local x0 = value // 128 ^ 0 % 128
|
|
146
|
+
local x1 = value // 128 ^ 1 % 128
|
|
147
|
+
local x2 = value // 128 ^ 2 % 128
|
|
148
|
+
local x3 = value // 128 ^ 3 % 128
|
|
149
|
+
|
|
150
|
+
if x3 ~= 0 then
|
|
151
|
+
cursor.tryalloc(c, 4)
|
|
152
|
+
cursor.writeu32(c, x0 * 256 ^ 3 + x1 * 256 ^ 2 + x2 * 256 + x3 + 128)
|
|
153
|
+
elseif x2 ~= 0 then
|
|
154
|
+
cursor.tryalloc(c, 3)
|
|
155
|
+
cursor.writeu24(c, x0 * 256 ^ 2 + x1 * 256 + x2 + 128)
|
|
156
|
+
elseif x1 ~= 0 then
|
|
157
|
+
cursor.tryalloc(c, 2)
|
|
158
|
+
cursor.writeu16(c, x0 * 256 + x1 + 128)
|
|
159
|
+
else
|
|
160
|
+
cursor.tryalloc(c, 1)
|
|
161
|
+
cursor.writeu8(c, x0 + 128)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
function cursor.read_vlq(c: Cursor): number
|
|
166
|
+
local b = cursor.readu8(c)
|
|
167
|
+
if b >= 128 then
|
|
168
|
+
return b - 128
|
|
169
|
+
end
|
|
170
|
+
local x = b
|
|
171
|
+
|
|
172
|
+
b = cursor.readu8(c)
|
|
173
|
+
if b >= 128 then
|
|
174
|
+
return x + (b - 128) * 128
|
|
175
|
+
end
|
|
176
|
+
x += b * 128
|
|
177
|
+
|
|
178
|
+
b = cursor.readu8(c)
|
|
179
|
+
if b >= 128 then
|
|
180
|
+
return x + (b - 128) * 128 ^ 2
|
|
181
|
+
end
|
|
182
|
+
x += b * 128 ^ 2
|
|
183
|
+
|
|
184
|
+
b = cursor.readu8(c)
|
|
185
|
+
if b >= 128 then
|
|
186
|
+
return x + (b - 128) * 128 ^ 3
|
|
187
|
+
end
|
|
188
|
+
x += b * 128 ^ 3
|
|
189
|
+
|
|
190
|
+
error "vlq length too large"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
function cursor.close(c: Cursor): buffer
|
|
194
|
+
local final = buffer.create(c.offset)
|
|
195
|
+
buffer.copy(final, 0, c.buffer, 0, c.offset)
|
|
196
|
+
c.buffer = final
|
|
197
|
+
return final
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
utils.cursor = cursor
|
|
201
|
+
|
|
202
|
+
local bitmask = {}
|
|
203
|
+
bitmask.__index = bitmask
|
|
204
|
+
|
|
205
|
+
export type Bitmask = {
|
|
206
|
+
buffer: buffer,
|
|
207
|
+
capacity: number,
|
|
208
|
+
entries: number,
|
|
209
|
+
} & typeof(bitmask)
|
|
210
|
+
|
|
211
|
+
local BITS_PER_ENTRY = 32
|
|
212
|
+
local BYTES_PER_ENTRY = 4
|
|
213
|
+
|
|
214
|
+
function get_entry_bit(index: number): (number, number)
|
|
215
|
+
local entry_index = math.floor(index / BITS_PER_ENTRY)
|
|
216
|
+
local bit_position = index % BITS_PER_ENTRY
|
|
217
|
+
return entry_index, bit_position
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
function bitmask.create(capacity: number?): Bitmask
|
|
221
|
+
local entries_needed = math.ceil(capacity :: number / BITS_PER_ENTRY)
|
|
222
|
+
local buffer_size = entries_needed * BYTES_PER_ENTRY
|
|
223
|
+
|
|
224
|
+
local self = {
|
|
225
|
+
buffer = buffer.create(buffer_size),
|
|
226
|
+
capacity = capacity,
|
|
227
|
+
entries = entries_needed,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return setmetatable(self, bitmask) :: any
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
function bitmask.from_set(indexes: { [any]: number }, filter: { [any]: boolean }, capacity: number?): Bitmask
|
|
234
|
+
local newmask = bitmask.create(capacity or 32)
|
|
235
|
+
for player in filter do
|
|
236
|
+
local index = indexes[player]
|
|
237
|
+
if index == nil then
|
|
238
|
+
continue
|
|
239
|
+
end
|
|
240
|
+
bitmask.set(newmask, index)
|
|
241
|
+
end
|
|
242
|
+
return newmask
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
function bitmask.expand(self: Bitmask, capacity: number)
|
|
246
|
+
if capacity <= self.capacity then
|
|
247
|
+
return
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
local new_entries = math.ceil(capacity / BITS_PER_ENTRY)
|
|
251
|
+
local new_buffer_size = new_entries * BYTES_PER_ENTRY
|
|
252
|
+
local new_buffer = buffer.create(new_buffer_size)
|
|
253
|
+
|
|
254
|
+
buffer.copy(new_buffer, 0, self.buffer, 0)
|
|
255
|
+
|
|
256
|
+
for i = self.entries * BYTES_PER_ENTRY, new_buffer_size - 1 do
|
|
257
|
+
buffer.writeu8(new_buffer, i, 0)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
self.buffer = new_buffer
|
|
261
|
+
self.capacity = capacity
|
|
262
|
+
self.entries = new_entries
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
function bitmask.read(self: Bitmask, index: number): number
|
|
266
|
+
if index >= self.entries then
|
|
267
|
+
return 0
|
|
268
|
+
end
|
|
269
|
+
return buffer.readu32(self.buffer, index * BYTES_PER_ENTRY)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
function bitmask.write(self: Bitmask, index: number, value: number)
|
|
273
|
+
if index >= self.entries then
|
|
274
|
+
--
|
|
275
|
+
bitmask.expand(self, (index + 1) * BITS_PER_ENTRY)
|
|
276
|
+
end
|
|
277
|
+
buffer.writeu32(self.buffer, index * BYTES_PER_ENTRY, value)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
-- BIT MANIPULATIONS
|
|
281
|
+
|
|
282
|
+
function bitmask.set(self: Bitmask, index: number)
|
|
283
|
+
if index < 0 then
|
|
284
|
+
error "Bit index cannot be negative"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
local entry_index, bit_position = get_entry_bit(index)
|
|
288
|
+
local current_value = bitmask.read(self, entry_index)
|
|
289
|
+
local new_value = bit32.bor(current_value, bit32.lshift(1, bit_position))
|
|
290
|
+
bitmask.write(self, entry_index, new_value)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
function bitmask.get(self: Bitmask, index: number): boolean
|
|
294
|
+
if index < 0 then
|
|
295
|
+
error "Bit index cannot be negative"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
local entry_index, bit_position = get_entry_bit(index)
|
|
299
|
+
if entry_index >= self.entries then
|
|
300
|
+
return false
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
local current_value = bitmask.read(self, entry_index)
|
|
304
|
+
return bit32.band(current_value, bit32.lshift(1, bit_position)) ~= 0
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
function bitmask.clear(self: Bitmask, index: number)
|
|
308
|
+
if index < 0 then
|
|
309
|
+
error "Bit index cannot be negative"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
local entry_index, bit_position = get_entry_bit(index)
|
|
313
|
+
if entry_index >= self.entries then
|
|
314
|
+
return
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
local current_value = bitmask.read(self, entry_index)
|
|
318
|
+
local mask = bit32.bnot(bit32.lshift(1, bit_position))
|
|
319
|
+
local new_value = bit32.band(current_value, mask)
|
|
320
|
+
bitmask.write(self, entry_index, new_value)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
-- BITWISE OPERATIONS
|
|
324
|
+
|
|
325
|
+
function bitmask.band(self: Bitmask, other: Bitmask): Bitmask
|
|
326
|
+
local max_entries = math.max(self.entries, other.entries)
|
|
327
|
+
local result = bitmask.create(max_entries * BITS_PER_ENTRY)
|
|
328
|
+
|
|
329
|
+
for i = 0, max_entries - 1 do
|
|
330
|
+
local value1 = bitmask.read(self, i)
|
|
331
|
+
local value2 = bitmask.read(other, i)
|
|
332
|
+
bitmask.write(result, i, bit32.band(value1, value2))
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
return result
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
function bitmask.bor(self: Bitmask, other: Bitmask): Bitmask
|
|
339
|
+
local max_entries = math.max(self.entries, other.entries)
|
|
340
|
+
local result = bitmask.create(max_entries * BITS_PER_ENTRY)
|
|
341
|
+
|
|
342
|
+
for i = 0, max_entries - 1 do
|
|
343
|
+
local value1 = bitmask.read(self, i)
|
|
344
|
+
local value2 = bitmask.read(other, i)
|
|
345
|
+
bitmask.write(result, i, bit32.bor(value1, value2))
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
return result
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
function bitmask.bxor(self: Bitmask, other: Bitmask): Bitmask
|
|
352
|
+
local max_entries = math.max(self.entries, other.entries)
|
|
353
|
+
local result = bitmask.create(max_entries * BITS_PER_ENTRY)
|
|
354
|
+
|
|
355
|
+
for i = 0, max_entries - 1 do
|
|
356
|
+
local value1 = bitmask.read(self, i)
|
|
357
|
+
local value2 = bitmask.read(other, i)
|
|
358
|
+
bitmask.write(result, i, bit32.bxor(value1, value2))
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
return result
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
function bitmask.bnot(self: Bitmask): Bitmask
|
|
365
|
+
local result = bitmask.create(self.capacity)
|
|
366
|
+
|
|
367
|
+
for i = 0, self.entries - 1 do
|
|
368
|
+
local value = bitmask.read(self, i)
|
|
369
|
+
bitmask.write(result, i, bit32.bnot(value))
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
return result
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
function bitmask.clone(self: Bitmask): Bitmask
|
|
376
|
+
local result = bitmask.create(self.capacity)
|
|
377
|
+
buffer.copy(result.buffer, 0, self.buffer, 0, self.entries * BYTES_PER_ENTRY)
|
|
378
|
+
return result
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
function bitmask.remap(self: Bitmask, from: { [any]: number }, to: { [any]: number }, new_capacity: number?): Bitmask
|
|
382
|
+
local result = bitmask.create(new_capacity or BITS_PER_ENTRY)
|
|
383
|
+
for member, index in to do
|
|
384
|
+
local old_index = from[member]
|
|
385
|
+
if old_index then
|
|
386
|
+
local old_bit = bitmask.get(self, old_index)
|
|
387
|
+
if old_bit then
|
|
388
|
+
bitmask.set(result, index)
|
|
389
|
+
else
|
|
390
|
+
bitmask.clear(result, index)
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
return result
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
function bitmask.lshift(self: Bitmask, n: number): Bitmask
|
|
398
|
+
if n <= 0 then
|
|
399
|
+
return bitmask.clone(self)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
local entry_shift = math.floor(n / BITS_PER_ENTRY)
|
|
403
|
+
local bit_shift = n % BITS_PER_ENTRY
|
|
404
|
+
|
|
405
|
+
local result = bitmask.create(self.capacity + n)
|
|
406
|
+
|
|
407
|
+
if bit_shift == 0 then
|
|
408
|
+
for i = 0, self.entries - 1 do
|
|
409
|
+
local value = bitmask.read(self, i)
|
|
410
|
+
bitmask.write(result, i + entry_shift, value)
|
|
411
|
+
end
|
|
412
|
+
else
|
|
413
|
+
local carry = 0
|
|
414
|
+
for i = 0, self.entries - 1 do
|
|
415
|
+
local value = bitmask.read(self, i)
|
|
416
|
+
local shifted_value = bit32.lshift(value, bit_shift)
|
|
417
|
+
local new_carry = bit32.rshift(value, BITS_PER_ENTRY - bit_shift)
|
|
418
|
+
|
|
419
|
+
bitmask.write(result, i + entry_shift, bit32.bor(shifted_value, carry))
|
|
420
|
+
carry = new_carry
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
if carry ~= 0 then
|
|
424
|
+
bitmask.write(result, self.entries + entry_shift, carry)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
return result
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
function bitmask.rshift(self: Bitmask, n: number): Bitmask
|
|
432
|
+
if n <= 0 then
|
|
433
|
+
return bitmask.clone(self)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
local entry_shift = math.floor(n / BITS_PER_ENTRY)
|
|
437
|
+
local bit_shift = n % BITS_PER_ENTRY
|
|
438
|
+
|
|
439
|
+
if entry_shift >= self.entries then
|
|
440
|
+
return bitmask.create(32)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
local result = bitmask.create(self.capacity)
|
|
444
|
+
|
|
445
|
+
if bit_shift == 0 then
|
|
446
|
+
for i = entry_shift, self.entries - 1 do
|
|
447
|
+
local value = bitmask.read(self, i)
|
|
448
|
+
bitmask.write(result, i - entry_shift, value)
|
|
449
|
+
end
|
|
450
|
+
else
|
|
451
|
+
for i = entry_shift, self.entries - 1 do
|
|
452
|
+
local value = bitmask.read(self, i)
|
|
453
|
+
local shifted_value = bit32.rshift(value, bit_shift)
|
|
454
|
+
|
|
455
|
+
local next_value = 0
|
|
456
|
+
if i + 1 < self.entries then
|
|
457
|
+
next_value = bitmask.read(self, i + 1)
|
|
458
|
+
end
|
|
459
|
+
local overflow = bit32.lshift(next_value, BITS_PER_ENTRY - bit_shift)
|
|
460
|
+
|
|
461
|
+
bitmask.write(result, i - entry_shift, bit32.bor(shifted_value, overflow))
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
return result
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
local function tobinary(n: number): string
|
|
469
|
+
if n == 0 then
|
|
470
|
+
return "0"
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
local result = ""
|
|
474
|
+
while n > 0 do
|
|
475
|
+
result = (n % 2) .. result
|
|
476
|
+
n = math.floor(n / 2)
|
|
477
|
+
end
|
|
478
|
+
return string.rep("0", 8 - #result) .. result
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
function bitmask.tostring(self: Bitmask): string
|
|
482
|
+
local highest_entry = -1
|
|
483
|
+
for i = self.entries - 1, 0, -1 do
|
|
484
|
+
local value = bitmask.read(self, i)
|
|
485
|
+
if value ~= 0 then
|
|
486
|
+
highest_entry = i
|
|
487
|
+
break
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
if highest_entry == -1 then
|
|
492
|
+
return ""
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
local str = ""
|
|
496
|
+
for i = 0, highest_entry do
|
|
497
|
+
local value = bitmask.read(self, i)
|
|
498
|
+
str ..= tobinary(value)
|
|
499
|
+
end
|
|
500
|
+
return str
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
utils.bitmask = {
|
|
504
|
+
create = bitmask.create,
|
|
505
|
+
from_set = bitmask.from_set,
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function utils.create_shared_lookup(world: World, components: common.Components)
|
|
509
|
+
local shared: common.Shared = {
|
|
510
|
+
lookup = {},
|
|
511
|
+
serdes = {},
|
|
512
|
+
bytespan = {},
|
|
513
|
+
ids = {},
|
|
514
|
+
components = {},
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
for component, name in world:query(ECS_NAME):with(components.shared):iter() do
|
|
518
|
+
shared.lookup[name] = component
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
for component, serdes in world:query(components.serdes):with(components.shared, ECS_NAME):iter() do
|
|
522
|
+
shared.serdes[component] = serdes
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
for component, bytespan in world:query(components.bytespan):with(components.shared, ECS_NAME):iter() do
|
|
526
|
+
shared.bytespan[component] = bytespan
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
local keys = {}
|
|
530
|
+
local component_ids = {}
|
|
531
|
+
local id_components = {}
|
|
532
|
+
|
|
533
|
+
for name in shared.lookup do
|
|
534
|
+
table.insert(keys, name)
|
|
535
|
+
end
|
|
536
|
+
table.sort(keys)
|
|
537
|
+
|
|
538
|
+
for id, name in keys do
|
|
539
|
+
local component = shared.lookup[name]
|
|
540
|
+
component_ids[shared.lookup[name]] = id
|
|
541
|
+
id_components[id] = component
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
component_ids[CHILD_OF] = #keys + 1
|
|
545
|
+
|
|
546
|
+
shared.ids = component_ids
|
|
547
|
+
shared.components = id_components
|
|
548
|
+
return shared
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
function utils.vlq_span(length: number): number
|
|
552
|
+
if length < 0x80 then
|
|
553
|
+
return 1
|
|
554
|
+
elseif length < 0x4000 then
|
|
555
|
+
return 2
|
|
556
|
+
elseif length < 0x200000 then
|
|
557
|
+
return 3
|
|
558
|
+
else
|
|
559
|
+
return 4
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
function utils.number_span(n: number): number
|
|
564
|
+
if n <= 0xFF then
|
|
565
|
+
return 1
|
|
566
|
+
elseif n <= 0xFFFF then
|
|
567
|
+
return 2
|
|
568
|
+
else
|
|
569
|
+
return 4 -- cant read 24 bits
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
function utils.read_number(buf: buffer, offset: number, span: number): (number, number)
|
|
574
|
+
if span == 1 then
|
|
575
|
+
return buffer.readu8(buf, offset), offset + 1
|
|
576
|
+
elseif span == 2 then
|
|
577
|
+
return buffer.readu16(buf, offset), offset + 2
|
|
578
|
+
else
|
|
579
|
+
return buffer.readu32(buf, offset), offset + 4
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
function utils.remove_all_relations(world: World, entity: Entity, relation: Entity)
|
|
584
|
+
local entity_index = jecs.entity_index_try_get_fast(world.entity_index :: any, entity :: any)
|
|
585
|
+
if not entity_index then
|
|
586
|
+
return
|
|
587
|
+
end
|
|
588
|
+
local archetype = entity_index.archetype
|
|
589
|
+
|
|
590
|
+
local wildcard = PAIR(relation, WILDCARD) :: any
|
|
591
|
+
local idr = world.component_index[wildcard]
|
|
592
|
+
if not idr then
|
|
593
|
+
return
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
local archetype_id = archetype.id
|
|
597
|
+
local count = idr.counts[archetype_id]
|
|
598
|
+
if not count then
|
|
599
|
+
return
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
local start = idr.records[archetype_id]
|
|
603
|
+
if not start then
|
|
604
|
+
return
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
for i = start, start + count - 1 do
|
|
608
|
+
local pair = archetype.types[i]
|
|
609
|
+
world:remove(entity, pair :: any)
|
|
610
|
+
end
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
function utils.logerror(...: string): never
|
|
614
|
+
error("REPLECS ERROR - " .. table.concat({ ... }, " "))
|
|
615
|
+
end
|
|
616
|
+
function utils.logwarn(...: string)
|
|
617
|
+
warn("REPLECS WARN - " .. table.concat({ ... }, " "))
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
function utils.logcomponent(world: World, component: Entity)
|
|
621
|
+
return ` name: {world:get(component, ECS_NAME) or "(no name)"} id: {component}`
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
return utils
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rbxts/replecs",
|
|
3
|
+
"version": "0.0.1-rc.0",
|
|
4
|
+
"description": "Replication library for Jecs",
|
|
5
|
+
"main": "out/init.lua",
|
|
6
|
+
"types": "out/index.d.ts",
|
|
7
|
+
"directories": {
|
|
8
|
+
"doc": "docs",
|
|
9
|
+
"test": "tests"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"out",
|
|
16
|
+
"!**/*.tsbuildinfo",
|
|
17
|
+
"!**/*.spec.lua",
|
|
18
|
+
"!**/*.spec.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"serve": "rojo sourcemap serve.project.json -o sourcemap.json --watch && rojo serve serve.project.json",
|
|
22
|
+
"build": "rbxtsc",
|
|
23
|
+
"watch": "rbxtsc -w",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/PepeElToro41/replecs.git"
|
|
29
|
+
},
|
|
30
|
+
"author": "PepeElToro41",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/PepeElToro41/replecs/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/PepeElToro41/replecs#readme",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@rbxts/compiler-types": "^2.3.0-types.1",
|
|
38
|
+
"@rbxts/types": "^1.0.768",
|
|
39
|
+
"roblox-ts": "3.0.0",
|
|
40
|
+
"typescript": "=5.5.3",
|
|
41
|
+
"prettier": "^3.2.5"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@rbxts/jecs": "^0.8.3"
|
|
45
|
+
}
|
|
46
|
+
}
|