@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
package/out/index.d.ts
ADDED
package/out/init.luau
ADDED
package/out/jecs.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@rbxts/jecs";
|
package/out/jecs.luau
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
--!native
|
|
2
|
+
--!optimize 2
|
|
3
|
+
|
|
4
|
+
local jecs = require(script.Parent.Parent.jecs)
|
|
5
|
+
local common = require(script.Parent.common)
|
|
6
|
+
local utils = require(script.Parent.utils)
|
|
7
|
+
|
|
8
|
+
local cursor = utils.cursor
|
|
9
|
+
type Cursor = utils.Cursor
|
|
10
|
+
|
|
11
|
+
type Entity<T = any> = jecs.Entity<T>
|
|
12
|
+
type World = jecs.World
|
|
13
|
+
|
|
14
|
+
export type Client = {
|
|
15
|
+
world: World,
|
|
16
|
+
inited: boolean?,
|
|
17
|
+
is_replicating: boolean,
|
|
18
|
+
after_replication_callbacks: { () -> () },
|
|
19
|
+
|
|
20
|
+
components: common.Components,
|
|
21
|
+
shared: common.Shared,
|
|
22
|
+
server_ids: { [number]: Entity },
|
|
23
|
+
client_ids: { [Entity]: number },
|
|
24
|
+
ordered_creation: boolean,
|
|
25
|
+
custom_ids: { [Entity]: (any) -> Entity },
|
|
26
|
+
|
|
27
|
+
init: (self: Client, world: World?) -> (),
|
|
28
|
+
destroy: (self: Client) -> (),
|
|
29
|
+
after_replication: (self: Client, callback: () -> ()) -> (),
|
|
30
|
+
|
|
31
|
+
apply_updates: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
32
|
+
apply_unreliable: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
33
|
+
apply_full: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
local ENTITY_ID_TYPES = {
|
|
37
|
+
id = 1,
|
|
38
|
+
custom = 2,
|
|
39
|
+
shared = 3,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
local ECS_NAME = jecs.Name
|
|
43
|
+
|
|
44
|
+
local function PAIR(first: Entity, second: Entity): Entity
|
|
45
|
+
return jecs.pair(first, second)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
local function get_or_create_entity(client: Client, server_id: number): Entity
|
|
49
|
+
local server_entity = client.server_ids[server_id]
|
|
50
|
+
if server_entity then
|
|
51
|
+
return server_entity
|
|
52
|
+
else
|
|
53
|
+
local entity = client.world:entity()
|
|
54
|
+
client.server_ids[server_id] = entity
|
|
55
|
+
client.client_ids[entity] = server_id
|
|
56
|
+
return entity
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
local function read_component_id(client: Client, c: Cursor)
|
|
61
|
+
local shared_id = cursor.readu8(c)
|
|
62
|
+
local component = client.shared.components[shared_id]
|
|
63
|
+
if not component then
|
|
64
|
+
print("NON SHARED COMPONENT", shared_id, client.shared.components)
|
|
65
|
+
error "received a non shared component"
|
|
66
|
+
end
|
|
67
|
+
return component
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
local function read_component_value(client: Client, c: Cursor, variants: { any }?): (Entity, any)
|
|
71
|
+
local component = read_component_id(client, c)
|
|
72
|
+
local serdes = client.shared.serdes[component]
|
|
73
|
+
|
|
74
|
+
if serdes then
|
|
75
|
+
local bytespan = client.shared.bytespan[component] or cursor.read_vlq(c)
|
|
76
|
+
|
|
77
|
+
local appended = cursor.read_buffer(c, bytespan)
|
|
78
|
+
local output = serdes.deserialize(appended)
|
|
79
|
+
return component, output
|
|
80
|
+
else
|
|
81
|
+
local variant_id = cursor.read_vlq(c)
|
|
82
|
+
if variant_id == 0 then
|
|
83
|
+
return component, nil
|
|
84
|
+
end
|
|
85
|
+
local value = (variants :: { any })[variant_id]
|
|
86
|
+
return component, value
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
local function read_entity_id(client: Client, c: Cursor, variants: { any }?): (Entity?, number?)
|
|
91
|
+
local id_type = cursor.readi8(c)
|
|
92
|
+
|
|
93
|
+
if id_type == ENTITY_ID_TYPES.id then
|
|
94
|
+
local server_id = cursor.readu24(c)
|
|
95
|
+
return client.server_ids[server_id], server_id
|
|
96
|
+
elseif id_type == ENTITY_ID_TYPES.custom then
|
|
97
|
+
local component, value = read_component_value(client, c, variants)
|
|
98
|
+
local custom_getter = client.custom_ids[component]
|
|
99
|
+
if not custom_getter then
|
|
100
|
+
error(
|
|
101
|
+
`received a custom id for a non custom component, consider adding custom_id to component: {client.world:get(
|
|
102
|
+
component,
|
|
103
|
+
ECS_NAME
|
|
104
|
+
) or "(no name)"} id: {component}`
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
return custom_getter(value), nil
|
|
108
|
+
elseif id_type == ENTITY_ID_TYPES.shared then
|
|
109
|
+
return read_component_id(client, c), nil
|
|
110
|
+
end
|
|
111
|
+
error("malformed entity id" .. cursor.readu32(c))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
local function process_entity_id(client: Client, c: Cursor, variants: { any }?)
|
|
115
|
+
local entity, server_id = read_entity_id(client, c, variants)
|
|
116
|
+
if entity then
|
|
117
|
+
return entity
|
|
118
|
+
else
|
|
119
|
+
return get_or_create_entity(client, server_id :: number)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
local function process_entity(client: Client, c: Cursor, variants: { any }?)
|
|
124
|
+
local entity = process_entity_id(client, c, variants)
|
|
125
|
+
|
|
126
|
+
local total_tags = cursor.read_vlq(c)
|
|
127
|
+
for i = 1, total_tags do
|
|
128
|
+
local tag = read_component_id(client, c)
|
|
129
|
+
client.world:add(entity, tag)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
local total_components = cursor.read_vlq(c)
|
|
133
|
+
for i = 1, total_components do
|
|
134
|
+
local component, value = read_component_value(client, c, variants)
|
|
135
|
+
client.world:set(entity, component, value)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
local total_pairs = cursor.read_vlq(c)
|
|
139
|
+
for i = 1, total_pairs do
|
|
140
|
+
local relation = read_component_id(client, c)
|
|
141
|
+
local total_targets = cursor.read_vlq(c)
|
|
142
|
+
|
|
143
|
+
for i = 1, total_targets do
|
|
144
|
+
local target = process_entity_id(client, c, variants)
|
|
145
|
+
client.world:add(entity, PAIR(relation, target))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
local function apply_updates(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
151
|
+
local c = cursor.from(buf)
|
|
152
|
+
local total_packets = cursor.read_vlq(c)
|
|
153
|
+
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
154
|
+
|
|
155
|
+
for a = 1, total_packets do
|
|
156
|
+
local variants = all_variants and all_variants[variant_start - a]
|
|
157
|
+
local total_added = cursor.read_vlq(c)
|
|
158
|
+
|
|
159
|
+
for i = 1, total_added do
|
|
160
|
+
process_entity(client, c, variants)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
local total_changed = cursor.read_vlq(c)
|
|
164
|
+
for i = 1, total_changed do
|
|
165
|
+
local entity = process_entity_id(client, c, variants)
|
|
166
|
+
local total_tagged = cursor.read_vlq(c)
|
|
167
|
+
for i = 1, total_tagged do
|
|
168
|
+
local tag = read_component_id(client, c)
|
|
169
|
+
client.world:add(entity, tag)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
local total_components = cursor.read_vlq(c)
|
|
173
|
+
for i = 1, total_components do
|
|
174
|
+
local component, value = read_component_value(client, c, variants)
|
|
175
|
+
client.world:set(entity, component, value)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
local total_pairs = cursor.read_vlq(c)
|
|
179
|
+
for i = 1, total_pairs do
|
|
180
|
+
local relation = read_component_id(client, c)
|
|
181
|
+
local total_targets = cursor.read_vlq(c)
|
|
182
|
+
|
|
183
|
+
for t = 1, total_targets do
|
|
184
|
+
local id_type = cursor.readi8(c)
|
|
185
|
+
-- here I use negative numbers to indicate removals
|
|
186
|
+
-- but "process_entity_id" expects a positive value
|
|
187
|
+
-- so I just peek at it and put it back but positive
|
|
188
|
+
-- (surely there is a better way to do this)
|
|
189
|
+
cursor.writei8(c, math.abs(id_type))
|
|
190
|
+
local target = process_entity_id(client, c, variants)
|
|
191
|
+
|
|
192
|
+
if id_type > 0 then
|
|
193
|
+
client.world:add(entity, PAIR(relation, target))
|
|
194
|
+
elseif id_type < 0 then
|
|
195
|
+
client.world:remove(entity, PAIR(relation, target))
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
local total_removed = cursor.read_vlq(c)
|
|
201
|
+
for i = 1, total_removed do
|
|
202
|
+
local component = read_component_id(client, c)
|
|
203
|
+
client.world:remove(entity, component)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
local total_component_deleted = cursor.read_vlq(c)
|
|
208
|
+
for i = 1, total_component_deleted do
|
|
209
|
+
local entity = process_entity_id(client, c, variants)
|
|
210
|
+
|
|
211
|
+
local total_tags = cursor.read_vlq(c)
|
|
212
|
+
for i = 1, total_tags do
|
|
213
|
+
local tag = read_component_id(client, c)
|
|
214
|
+
client.world:remove(entity, tag)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
local total_components = cursor.read_vlq(c)
|
|
218
|
+
for i = 1, total_components do
|
|
219
|
+
local component = read_component_id(client, c)
|
|
220
|
+
client.world:remove(entity, component)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
local total_pairs = cursor.read_vlq(c)
|
|
224
|
+
for i = 1, total_pairs do
|
|
225
|
+
local relation = read_component_id(client, c)
|
|
226
|
+
utils.remove_all_relations(client.world, entity, relation)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
local total_deleted = cursor.read_vlq(c)
|
|
231
|
+
for i = 1, total_deleted do
|
|
232
|
+
local entity, server_id = read_entity_id(client, c, variants)
|
|
233
|
+
if entity then
|
|
234
|
+
client.world:delete(entity)
|
|
235
|
+
end
|
|
236
|
+
if server_id then
|
|
237
|
+
client.server_ids[server_id] = nil
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
local function apply_unreliable(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
244
|
+
local c = cursor.from(buf)
|
|
245
|
+
local total_packets = cursor.read_vlq(c)
|
|
246
|
+
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
247
|
+
|
|
248
|
+
for a = 1, total_packets do
|
|
249
|
+
local total_entities = cursor.read_vlq(c)
|
|
250
|
+
local variants = all_variants and all_variants[variant_start - a]
|
|
251
|
+
|
|
252
|
+
for e = 1, total_entities do
|
|
253
|
+
local entity = read_entity_id(client, c, variants)
|
|
254
|
+
local total_unreliable = cursor.read_vlq(c)
|
|
255
|
+
|
|
256
|
+
if entity then
|
|
257
|
+
local component_id, value = read_component_value(client, c, variants)
|
|
258
|
+
client.world:set(entity, component_id, value)
|
|
259
|
+
else
|
|
260
|
+
-- dont apply unreliable updates to entities that dont exist in the client yet
|
|
261
|
+
-- but we still need to process the values to keep the cursor in the right place
|
|
262
|
+
for u = 1, total_unreliable do
|
|
263
|
+
read_component_value(client, c, variants)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
local function apply_full(client: Client, buf: buffer, all_variants: { { any } }?)
|
|
271
|
+
local c = cursor.from(buf)
|
|
272
|
+
local total_packets = cursor.read_vlq(c)
|
|
273
|
+
local variant_start = (all_variants and #all_variants + 1) :: number
|
|
274
|
+
|
|
275
|
+
for a = 1, total_packets do
|
|
276
|
+
local total_entities = cursor.read_vlq(c)
|
|
277
|
+
local variants = all_variants and all_variants[variant_start - a]
|
|
278
|
+
|
|
279
|
+
for e = 1, total_entities do
|
|
280
|
+
process_entity(client, c, variants)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
local function init(client: Client, _world: World?)
|
|
286
|
+
if client.inited == true then
|
|
287
|
+
return warn "attempted to init a client twice"
|
|
288
|
+
end
|
|
289
|
+
if client.inited == nil then
|
|
290
|
+
return warn "attempted to re-init a destroyed client"
|
|
291
|
+
end
|
|
292
|
+
client.inited = true :: any
|
|
293
|
+
|
|
294
|
+
local world = _world or client.world
|
|
295
|
+
client.world = world
|
|
296
|
+
|
|
297
|
+
if not world then
|
|
298
|
+
error "Providing a world is required to start replecs"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
local components = client.components
|
|
302
|
+
|
|
303
|
+
client.shared = utils.create_shared_lookup(world, components)
|
|
304
|
+
|
|
305
|
+
for component, custom_id_get in world:query(components.custom_id):with(components.shared, ECS_NAME) do
|
|
306
|
+
client.custom_ids[component] = custom_id_get
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
local function after_replication(client: Client, callback: () -> ())
|
|
311
|
+
if client.is_replicating then
|
|
312
|
+
table.insert(client.after_replication_callbacks, callback)
|
|
313
|
+
else
|
|
314
|
+
callback()
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
local function destroy(client: Client)
|
|
319
|
+
if client.inited == nil then
|
|
320
|
+
return warn "attempted to destroy a client twice"
|
|
321
|
+
end
|
|
322
|
+
client.inited = nil :: any
|
|
323
|
+
-- I'll add any logic here if I need to clean up something
|
|
324
|
+
-- right now this is kind of useless
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
local client = {}
|
|
328
|
+
client.__index = client
|
|
329
|
+
client.init = init
|
|
330
|
+
client.destroy = destroy
|
|
331
|
+
client.after_replication = after_replication
|
|
332
|
+
client.apply_updates = apply_updates
|
|
333
|
+
client.apply_unreliable = apply_unreliable
|
|
334
|
+
client.apply_full = apply_full
|
|
335
|
+
|
|
336
|
+
local function create(world: World?, components: common.Components): Client
|
|
337
|
+
local self = {} :: Client
|
|
338
|
+
|
|
339
|
+
self.components = components
|
|
340
|
+
self.ordered_creation = true
|
|
341
|
+
self.custom_ids = {}
|
|
342
|
+
self.server_ids = {}
|
|
343
|
+
self.client_ids = {}
|
|
344
|
+
self.world = world :: any
|
|
345
|
+
self.inited = false
|
|
346
|
+
self.is_replicating = false
|
|
347
|
+
self.after_replication_callbacks = {}
|
|
348
|
+
|
|
349
|
+
return setmetatable(self, client) :: any
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
client.create = create
|
|
353
|
+
|
|
354
|
+
return client :: { create: typeof(create) }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
local jecs = require(script.Parent.Parent.jecs)
|
|
2
|
+
|
|
3
|
+
export type PlayerFilter = {
|
|
4
|
+
[Player]: boolean,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type Serdes = {
|
|
8
|
+
serialize: (value: any) -> buffer,
|
|
9
|
+
deserialize: (buffer) -> any,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Components = {
|
|
13
|
+
shared: jecs.Entity,
|
|
14
|
+
networked: jecs.Entity<PlayerFilter?>,
|
|
15
|
+
reliable: jecs.Entity,
|
|
16
|
+
unreliable: jecs.Entity,
|
|
17
|
+
pair: jecs.Entity,
|
|
18
|
+
|
|
19
|
+
serdes: jecs.Entity<Serdes>,
|
|
20
|
+
bytespan: jecs.Entity<number>,
|
|
21
|
+
custom_id: jecs.Entity<(value: any) -> jecs.Entity>,
|
|
22
|
+
__alive_tracking__: jecs.Entity,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Shared = {
|
|
26
|
+
lookup: { [string]: jecs.Entity },
|
|
27
|
+
|
|
28
|
+
components: { [number]: jecs.Entity },
|
|
29
|
+
ids: { [jecs.Entity]: number },
|
|
30
|
+
|
|
31
|
+
bytespan: { [jecs.Entity]: number },
|
|
32
|
+
serdes: { [jecs.Entity]: Serdes },
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type WorldHooks = {
|
|
36
|
+
added: (
|
|
37
|
+
world: jecs.World,
|
|
38
|
+
component: jecs.Entity,
|
|
39
|
+
fn: (entity: jecs.Entity, id: jecs.Id, value: any) -> ()
|
|
40
|
+
) -> () -> (),
|
|
41
|
+
changed: (
|
|
42
|
+
world: jecs.World,
|
|
43
|
+
component: jecs.Entity,
|
|
44
|
+
fn: (entity: jecs.Entity, id: jecs.Id, value: any) -> ()
|
|
45
|
+
) -> () -> (),
|
|
46
|
+
removed: (world: jecs.World, component: jecs.Entity, fn: (entity: jecs.Entity, id: jecs.Id) -> ()) -> () -> (),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return nil
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Entity, Id, Tag, World } from "@rbxts/jecs";
|
|
2
|
+
|
|
3
|
+
declare namespace Replecs {
|
|
4
|
+
export interface SerdesTable {
|
|
5
|
+
serialize: (value: any) => buffer;
|
|
6
|
+
deserialize: (buffer: buffer) => any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ObserverWorld extends World {
|
|
10
|
+
added<T>(
|
|
11
|
+
this: ObserverWorld,
|
|
12
|
+
component: Id<T>,
|
|
13
|
+
callback: (e: Entity, id: Id<T>, value?: T) => void
|
|
14
|
+
): () => void;
|
|
15
|
+
removed<T>(
|
|
16
|
+
this: ObserverWorld,
|
|
17
|
+
component: Id<T>,
|
|
18
|
+
callback: (e: Entity, id: Id<T>) => void
|
|
19
|
+
): () => void;
|
|
20
|
+
changed<T>(
|
|
21
|
+
this: ObserverWorld,
|
|
22
|
+
component: Id<T>,
|
|
23
|
+
callback: (e: Entity, id: Id<T>, value?: T) => void
|
|
24
|
+
): () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type MemberFilter = Map<Player, boolean>;
|
|
28
|
+
|
|
29
|
+
export interface Components {
|
|
30
|
+
shared: Tag;
|
|
31
|
+
networked: Entity<MemberFilter | undefined>;
|
|
32
|
+
reliable: Entity<MemberFilter | undefined>;
|
|
33
|
+
unreliable: Entity<MemberFilter | undefined>;
|
|
34
|
+
pair: Entity<MemberFilter | undefined>;
|
|
35
|
+
|
|
36
|
+
serdes: Entity<SerdesTable>;
|
|
37
|
+
bytespan: Entity<number>;
|
|
38
|
+
custom_id: Entity<((value: any) => Entity) | undefined>;
|
|
39
|
+
|
|
40
|
+
Shared: Tag;
|
|
41
|
+
Networked: Entity<MemberFilter | undefined>;
|
|
42
|
+
Reliable: Entity<MemberFilter | undefined>;
|
|
43
|
+
Unreliable: Entity<MemberFilter | undefined>;
|
|
44
|
+
Pair: Entity<MemberFilter | undefined>;
|
|
45
|
+
|
|
46
|
+
Serdes: Entity<SerdesTable>;
|
|
47
|
+
Bytespan: Entity<number>;
|
|
48
|
+
CustomId: Entity<((value: any) => Entity) | undefined>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Client {
|
|
52
|
+
world: ObserverWorld;
|
|
53
|
+
inited?: boolean;
|
|
54
|
+
|
|
55
|
+
init(world?: ObserverWorld): void;
|
|
56
|
+
destroy(): void;
|
|
57
|
+
after_replication(callback: () => void): void;
|
|
58
|
+
|
|
59
|
+
apply_updates(buf: buffer, all_variants?: any[][]): void;
|
|
60
|
+
apply_unreliable(buf: buffer, all_variants?: any[][]): void;
|
|
61
|
+
apply_full(buf: buffer, all_variants?: any[][]): void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface Server {
|
|
65
|
+
world: ObserverWorld;
|
|
66
|
+
inited?: boolean;
|
|
67
|
+
|
|
68
|
+
init(world?: ObserverWorld): void;
|
|
69
|
+
destroy(): void;
|
|
70
|
+
|
|
71
|
+
get_full(player: Player): LuaTuple<[buffer, any[][]]>;
|
|
72
|
+
collect_updates(): IterableFunction<LuaTuple<[Player, buffer, any[][]]>>;
|
|
73
|
+
collect_unreliable(): IterableFunction<
|
|
74
|
+
LuaTuple<[Player, buffer, any[][]]>
|
|
75
|
+
>;
|
|
76
|
+
mark_player_ready(player: Player): void;
|
|
77
|
+
is_player_ready(player: Player): boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface ReplecsLib {
|
|
81
|
+
client: Client;
|
|
82
|
+
server: Server;
|
|
83
|
+
|
|
84
|
+
after_replication(world: ObserverWorld): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface Replecs extends Components {
|
|
88
|
+
create: (world?: ObserverWorld) => ReplecsLib;
|
|
89
|
+
create_server: (world?: ObserverWorld) => Server;
|
|
90
|
+
create_client: (world?: ObserverWorld) => Client;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
declare const Replecs: Replecs.Replecs;
|
|
95
|
+
|
|
96
|
+
export = Replecs;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
local RunService = game:GetService "RunService"
|
|
2
|
+
local jecs = require(script.Parent.jecs)
|
|
3
|
+
local common = require(script.common)
|
|
4
|
+
|
|
5
|
+
local client = require(script.client)
|
|
6
|
+
local server = require(script.server)
|
|
7
|
+
|
|
8
|
+
export type Server = server.Server
|
|
9
|
+
export type Client = client.Client
|
|
10
|
+
|
|
11
|
+
export type Replecs = {
|
|
12
|
+
server: Server,
|
|
13
|
+
client: Client,
|
|
14
|
+
|
|
15
|
+
after_replication: (self: Replecs, callback: () -> ()) -> (),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type MemberFilter = {
|
|
19
|
+
[Player]: boolean,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
local replecs = {
|
|
23
|
+
shared = jecs.tag(),
|
|
24
|
+
networked = jecs.component() :: jecs.Entity<MemberFilter?>,
|
|
25
|
+
reliable = jecs.component() :: jecs.Entity<MemberFilter?>,
|
|
26
|
+
unreliable = jecs.component() :: jecs.Entity<MemberFilter?>,
|
|
27
|
+
pair = jecs.tag(),
|
|
28
|
+
|
|
29
|
+
serdes = jecs.component() :: jecs.Entity<common.Serdes>,
|
|
30
|
+
bytespan = jecs.component() :: jecs.Entity<number>,
|
|
31
|
+
custom_id = jecs.component() :: jecs.Entity<(any) -> jecs.Entity>,
|
|
32
|
+
__alive_tracking__ = jecs.tag(),
|
|
33
|
+
}
|
|
34
|
+
replecs.__index = replecs
|
|
35
|
+
replecs.Serdes = replecs.serdes
|
|
36
|
+
replecs.Bytespan = replecs.bytespan
|
|
37
|
+
replecs.CustomId = replecs.custom_id
|
|
38
|
+
replecs.Networked = replecs.networked
|
|
39
|
+
replecs.Reliable = replecs.reliable
|
|
40
|
+
replecs.Unreliable = replecs.unreliable
|
|
41
|
+
replecs.Pair = replecs.pair
|
|
42
|
+
replecs.Shared = replecs.shared
|
|
43
|
+
|
|
44
|
+
jecs.meta(replecs.serdes, jecs.Name, "replecs.serdes")
|
|
45
|
+
jecs.meta(replecs.bytespan, jecs.Name, "replecs.bytespan")
|
|
46
|
+
jecs.meta(replecs.custom_id, jecs.Name, "replecs.custom_id")
|
|
47
|
+
jecs.meta(replecs.networked, jecs.Name, "replecs.networked")
|
|
48
|
+
jecs.meta(replecs.reliable, jecs.Name, "replecs.reliable")
|
|
49
|
+
jecs.meta(replecs.unreliable, jecs.Name, "replecs.unreliable")
|
|
50
|
+
jecs.meta(replecs.pair, jecs.Name, "replecs.pair")
|
|
51
|
+
jecs.meta(replecs.shared, jecs.Name, "replecs.shared")
|
|
52
|
+
|
|
53
|
+
function replecs.create_server(world: jecs.World?): Server
|
|
54
|
+
return server.create(world, replecs)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
function replecs.create_client(world: jecs.World?): Client
|
|
58
|
+
return client.create(world, replecs)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
function replecs.create(world: jecs.World?): Replecs
|
|
62
|
+
local lib = {} :: Replecs
|
|
63
|
+
|
|
64
|
+
if RunService:IsServer() then
|
|
65
|
+
lib.server = server.create(world, replecs)
|
|
66
|
+
end
|
|
67
|
+
if RunService:IsClient() then
|
|
68
|
+
lib.client = client.create(world, replecs)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
return setmetatable(lib, replecs) :: any
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
function replecs.after_replication(lib: Replecs, callback: () -> ())
|
|
75
|
+
local lib_client = lib.client
|
|
76
|
+
if lib_client then
|
|
77
|
+
lib_client:after_replication(callback)
|
|
78
|
+
else
|
|
79
|
+
callback()
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return replecs
|