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

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.
@@ -891,20 +891,11 @@ local function process_entity_updates(
891
891
  end
892
892
  end
893
893
 
894
- -- one update group: [u16 group mask, deleted ids, added ids, per entity
895
- -- blocks] in read order. deletions come first so a server id reused by an
896
- -- addition in the same group starts from a fresh entity
894
+ -- one update group: [u16 group mask, added ids, per entity blocks, deleted
895
+ -- ids] in read order. creations, then updates, then deletions
897
896
  local function process_update_group(self: Client, c: Cursor, custom_ids: CustomIdMap, variants: Array<any>?)
898
897
  local mask = cursor.readu16(c)
899
898
 
900
- local total_deleted = read_vlq_bitmask(c, mask, UPDATE_GROUP_BITS.deleted_entities)
901
- for _ = 1, total_deleted do
902
- local entity, server_id = find_entity(self, c)
903
- if entity then
904
- delete_entity(self, entity, server_id)
905
- end
906
- end
907
-
908
899
  local total_added = read_vlq_bitmask(c, mask, UPDATE_GROUP_BITS.added_entities)
909
900
  for _ = 1, total_added do
910
901
  process_entity(self, c, custom_ids)
@@ -916,6 +907,14 @@ local function process_update_group(self: Client, c: Cursor, custom_ids: CustomI
916
907
  process_entity_updates(self, entity, c, custom_ids, variants)
917
908
  end)
918
909
  end
910
+
911
+ local total_deleted = read_vlq_bitmask(c, mask, UPDATE_GROUP_BITS.deleted_entities)
912
+ for _ = 1, total_deleted do
913
+ local entity, server_id = find_entity(self, c)
914
+ if entity then
915
+ delete_entity(self, entity, server_id)
916
+ end
917
+ end
919
918
  end
920
919
 
921
920
  local function apply_command_buffer(
@@ -0,0 +1,297 @@
1
+ import type { Entity, Id, InferComponent, Pair, Tag, World } from "@rbxts/jecs";
2
+
3
+ declare namespace Replecs {
4
+ export type SerdesTable<T = any> =
5
+ | {
6
+ bytespan?: number;
7
+ includes_variants?: false;
8
+ serialize: (value: T) => buffer;
9
+ deserialize: (buffer: buffer) => T;
10
+ }
11
+ | {
12
+ bytespan?: number;
13
+ includes_variants: true;
14
+ serialize: (value: T) => LuaTuple<[buffer, defined[] | undefined]>;
15
+ deserialize: (buffer: buffer, blobs: defined[] | undefined) => T;
16
+ };
17
+
18
+ export type Serdes<T = any> = SerdesTable<T>;
19
+
20
+ export interface Preprocessor<B = any, A = any> {
21
+ serialize: (value: B) => A;
22
+ deserialize: (value: A) => B;
23
+ }
24
+
25
+ export interface DeltaSerializer<T = any, D = T> {
26
+ snapshot?: (value: T) => T;
27
+ delta: (old: T, curr: T) => D;
28
+ apply: (curr: T, delta: D) => T;
29
+ }
30
+
31
+ type MemberFilterMap = Map<Player, boolean>;
32
+ export type MemberFilterPredicate = (player: Player) => boolean;
33
+ type MemberFilter =
34
+ | Player
35
+ | MemberFilterMap
36
+ | MemberFilterPredicate
37
+ | Record<string, never>
38
+ | undefined;
39
+ type Member = unknown;
40
+
41
+ export interface SharedInfo<T> {
42
+ lookup: Record<string, T>;
43
+ keys: string[];
44
+ indexes: T[];
45
+ members: Map<T, number>;
46
+ }
47
+ export interface HandleContext {
48
+ entity_id: number;
49
+ component: <T>(component: Entity<T>) => T;
50
+ target: (relation: Id, index?: number) => Entity | undefined;
51
+ pair_value: <T>(relation: Id<T>, target: Entity) => T | undefined;
52
+ has_pair: (relation: Id, target: Entity) => boolean;
53
+
54
+ entity: (server_entity: number) => Entity | undefined;
55
+ has: (tag: Entity) => boolean;
56
+ }
57
+ export interface CustomId {
58
+ identifier: string;
59
+ handle_callback: (ctx: HandleContext) => Entity;
60
+ handle(handler: (ctx: HandleContext) => Entity): void;
61
+ }
62
+
63
+ export interface Shared {
64
+ components: SharedInfo<Entity>;
65
+ custom_ids: SharedInfo<CustomId>;
66
+ serdes: Map<Id, SerdesTable>;
67
+ }
68
+ interface HandshakeSerdesInfo {
69
+ includes_variants?: boolean;
70
+ bytespan?: number;
71
+ }
72
+
73
+ export interface HandshakeInfo {
74
+ components: Record<string, boolean>;
75
+ custom_ids: Record<string, boolean>;
76
+ serdes: Record<string, HandshakeSerdesInfo>;
77
+ }
78
+
79
+ export interface Components {
80
+ shared: Tag;
81
+ networked: Entity<MemberFilter>;
82
+ reliable: Entity<MemberFilter>;
83
+ unreliable: Entity<MemberFilter>;
84
+ once: Entity<MemberFilter>;
85
+ relation: Entity<MemberFilter>;
86
+
87
+ serdes: Entity<SerdesTable>;
88
+ custom: Entity;
89
+ custom_handler: Entity<(value: any) => Entity>;
90
+
91
+ Shared: Tag;
92
+ Networked: Entity<MemberFilter>;
93
+ Reliable: Entity<MemberFilter>;
94
+ Unreliable: Entity<MemberFilter>;
95
+ Once: Entity<MemberFilter>;
96
+ Relation: Entity<MemberFilter>;
97
+
98
+ Serdes: Entity<SerdesTable>;
99
+ Custom: Entity;
100
+ CustomHandler: Entity<(value: any) => Entity>;
101
+ }
102
+
103
+ interface SerializationOptions {
104
+ set_serdes<T extends Id>(
105
+ component: InferComponent<T>,
106
+ serdes: SerdesTable<T>,
107
+ ): void;
108
+ remove_serdes(component: Id): void;
109
+
110
+ set_preprocessor<T extends Id, A = any>(
111
+ component: T,
112
+ preprocessor: Preprocessor<InferComponent<T>, A>,
113
+ ): void;
114
+ remove_preprocessor(component: Id): void;
115
+
116
+ set_delta_serializer<T extends Id, D = InferComponent<T>>(
117
+ component: T,
118
+ delta: DeltaSerializer<InferComponent<T>, D>,
119
+ ): void;
120
+ remove_delta_serializer(component: Id): void;
121
+ }
122
+
123
+ export interface ClientImp extends SerializationOptions {}
124
+
125
+ export interface Client extends ClientImp {
126
+ world: World;
127
+ inited?: boolean;
128
+
129
+ is_replicating: boolean;
130
+ after_replication_callbacks: (() => void)[];
131
+
132
+ components: Components;
133
+ shared: Shared;
134
+
135
+ init(world?: World): void;
136
+ destroy(): void;
137
+ handle_global(handler: (id: number) => Entity): void;
138
+ get_server_entity(client_entity: Entity): number | undefined;
139
+ get_client_entity(server_entity: number): Entity | undefined;
140
+
141
+ register_entity(entity: Entity, server_entity: number): void;
142
+ unregister_entity(entity: Entity): void;
143
+
144
+ after_replication(callback: () => void): void;
145
+ added(callback: (entity: Entity) => void): () => void;
146
+
147
+ hook<T>(
148
+ action: "changed",
149
+ relation: Pair<MemberFilter, T>,
150
+ callback: (entity: Entity, id: Id<T>, value: T) => void,
151
+ ): () => void;
152
+ hook<T>(
153
+ action: "removed",
154
+ relation: Pair<MemberFilter, T>,
155
+ callback: (entity: Entity, id: Id<T>) => void,
156
+ ): () => void;
157
+ hook(
158
+ action: "deleted",
159
+ entity: Entity,
160
+ callback: (entity: Entity) => void,
161
+ ): () => void;
162
+
163
+ override<T>(
164
+ action: "changed",
165
+ relation: Pair<MemberFilter, T>,
166
+ callback: (entity: Entity, id: Id<T>, value: T) => void,
167
+ ): () => void;
168
+ override<T>(
169
+ action: "removed",
170
+ relation: Pair<MemberFilter, T>,
171
+ callback: (entity: Entity, id: Id<T>) => void,
172
+ ): () => void;
173
+ override(
174
+ action: "deleted",
175
+ entity: Entity,
176
+ callback: (entity: Entity) => void,
177
+ ): () => void;
178
+
179
+ encode_component(component: Entity): number;
180
+ decode_component(encoded: number): Entity | undefined;
181
+ get_shared_count(): number;
182
+
183
+ register_custom_id(custom_id: CustomId): void;
184
+
185
+ apply_updates(buf: buffer, all_variants?: defined[][]): void;
186
+ apply_unreliable(buf: buffer, all_variants?: defined[][]): void;
187
+ apply_full(buf: buffer, variants?: defined[]): void;
188
+ apply_entity(buf: buffer, all_variants?: defined[][]): void;
189
+
190
+ generate_handshake(): HandshakeInfo;
191
+ verify_handshake(
192
+ handshake: HandshakeInfo,
193
+ ): LuaTuple<[true]> | LuaTuple<[false, string]>;
194
+ }
195
+
196
+ export interface ServerImp extends SerializationOptions {
197
+ set_networked(entity: Entity, filter?: MemberFilter): void;
198
+ set_reliable(
199
+ entity: Entity,
200
+ component: Entity,
201
+ filter?: MemberFilter,
202
+ ): void;
203
+ set_unreliable(
204
+ entity: Entity,
205
+ component: Entity,
206
+ filter?: MemberFilter,
207
+ ): void;
208
+ set_once(entity: Entity, component: Entity, filter?: MemberFilter): void;
209
+ set_pair(entity: Entity, id: Pair, filter?: MemberFilter): void;
210
+ set_unreliable_pair(entity: Entity, id: Pair, filter?: MemberFilter): void;
211
+ set_relation(entity: Entity, relation: Entity, filter?: MemberFilter): void;
212
+
213
+ stop_networked(entity: Entity): void;
214
+ stop_reliable(entity: Entity, component: Entity): void;
215
+ stop_unreliable(entity: Entity, component: Entity): void;
216
+ stop_once(entity: Entity, component: Entity): void;
217
+ stop_pair(entity: Entity, id: Pair): void;
218
+ stop_unreliable_pair(entity: Entity, id: Pair): void;
219
+ stop_relation(entity: Entity, relation: Entity): void;
220
+
221
+ /** Marks a reliable/once component dirty so its current value replicates again. */
222
+ force_change(entity: Entity, component: Entity): void;
223
+
224
+ set_global(entity: Entity, global_id: number): void;
225
+ remove_global(entity: Entity): void;
226
+ set_custom(entity: Entity, handler: Entity | CustomId): void;
227
+ remove_custom(entity: Entity): void;
228
+ }
229
+
230
+ export interface Server extends ServerImp {
231
+ world: World;
232
+ inited?: boolean;
233
+
234
+ components: Components;
235
+ shared: Shared;
236
+
237
+ init(world?: World): void;
238
+ destroy(): void;
239
+
240
+ encode_component(component: Entity): number;
241
+ decode_component(encoded: number): Entity | undefined;
242
+ get_shared_count(): number;
243
+
244
+ register_custom_id(custom_id: CustomId): void;
245
+
246
+ get_full(player: Player): LuaTuple<[buffer, defined[]]>;
247
+ collect_entity(
248
+ entity: Entity,
249
+ ): IterableFunction<LuaTuple<[Player, buffer, defined[][] | undefined]>>;
250
+ collect_updates(): IterableFunction<
251
+ LuaTuple<[Player, buffer, defined[][] | undefined]>
252
+ >;
253
+ collect_unreliable(): IterableFunction<
254
+ LuaTuple<[Player, buffer, defined[][] | undefined]>
255
+ >;
256
+
257
+ register_player(player: Player): void;
258
+ remove_player(player: Player): void;
259
+ mark_player_ready(player: Player): void;
260
+ is_player_ready(player: Player): boolean;
261
+
262
+ add_player_alias(client: Player, alias: defined): void;
263
+ remove_player_alias(alias: defined): void;
264
+
265
+ generate_handshake(): HandshakeInfo;
266
+ verify_handshake(
267
+ handshake: HandshakeInfo,
268
+ ): LuaTuple<[true]> | LuaTuple<[false, string]>;
269
+ }
270
+
271
+ export interface ReplecsLib extends SerializationOptions {
272
+ client: Client;
273
+ server: Server;
274
+ components: Components;
275
+
276
+ after_replication(callback: () => void): void;
277
+ register_custom_id(custom_id: CustomId): void;
278
+ }
279
+
280
+ export interface Replecs extends Components {
281
+ VERSION: string;
282
+ /** Client cap used to size the server bitmasks. Read at `create` / `create_server`. */
283
+ MAX_CLIENTS: number;
284
+
285
+ create: (world?: World) => ReplecsLib;
286
+ create_server: (world?: World) => Server;
287
+ create_client: (world?: World) => Client;
288
+ create_custom_id: (
289
+ identifier: string,
290
+ handler?: (ctx: HandleContext) => Entity,
291
+ ) => CustomId;
292
+ }
293
+ }
294
+
295
+ declare const Replecs: Replecs.Replecs;
296
+
297
+ export = Replecs;
@@ -2263,7 +2263,6 @@ end
2263
2263
 
2264
2264
  -- layout (read order is the reverse of write order):
2265
2265
  -- u16 group mask GROUP_BITS, vlq count per set bit
2266
- -- deleted entities entity ids
2267
2266
  -- added entities entity ids
2268
2267
  -- updates per entity:
2269
2268
  -- entity id
@@ -2276,12 +2275,20 @@ end
2276
2275
  -- changed / added / removed, each vlq counted
2277
2276
  -- full relations components then tags, each vlq counted
2278
2277
  -- relations removed relation ids, vlq
2278
+ -- deleted entities entity ids
2279
2279
  local function serialize_update_group(self: Server, group: SerializeGroup): (buffer, Array<any>)
2280
2280
  local c = cursor.new()
2281
2281
  local variants: Array<any> = {}
2282
2282
  local group_mask = 0
2283
2283
 
2284
2284
  local total = 0
2285
+ for _, entity in group.deleted_entities do
2286
+ write_entity_id(self, c, entity)
2287
+ total += 1
2288
+ end
2289
+ group_mask = write_vlq_bitmask(c, total, group_mask, GROUP_BITS.deleted_entities)
2290
+
2291
+ total = 0
2285
2292
  for entity, entry in group.updates do
2286
2293
  serialize_entity_updates(self, c, entity, entry, variants)
2287
2294
  total += 1
@@ -2295,13 +2302,6 @@ local function serialize_update_group(self: Server, group: SerializeGroup): (buf
2295
2302
  end
2296
2303
  group_mask = write_vlq_bitmask(c, total, group_mask, GROUP_BITS.added_entities)
2297
2304
 
2298
- total = 0
2299
- for _, entity in group.deleted_entities do
2300
- write_entity_id(self, c, entity)
2301
- total += 1
2302
- end
2303
- group_mask = write_vlq_bitmask(c, total, group_mask, GROUP_BITS.deleted_entities)
2304
-
2305
2305
  cursor.writeu16(c, group_mask)
2306
2306
 
2307
2307
  return cursor.close(c), variants
@@ -1 +1 @@
1
- return "v0.4.0-rc.2"
1
+ return "v0.4.0-rc.3"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rbxts/replecs",
3
- "version": "0.4.0-rc.2",
3
+ "version": "0.4.0-rc.3",
4
4
  "description": "Replication library for Jecs",
5
5
  "main": "out/init.luau",
6
6
  "types": "out/index.d.ts",
@@ -21,8 +21,7 @@
21
21
  "out",
22
22
  "!**/*.spec.lua",
23
23
  "!**/*.spec.d.ts",
24
- "!**/*.tsbuildinfo",
25
- "!out/replecs/index.d.ts"
24
+ "!**/*.tsbuildinfo"
26
25
  ],
27
26
  "scripts": {
28
27
  "serve": "rojo sourcemap serve.project.json -o sourcemap.json --watch && rojo serve serve.project.json"