@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.
- package/out/replecs/client/init.luau +22 -16
- package/out/replecs/handshake.luau +35 -0
- package/out/replecs/index.d.ts +43 -0
- package/out/replecs/init.luau +8 -0
- package/out/replecs/server/filter_group.luau +297 -123
- package/out/replecs/server/init.luau +177 -140
- package/out/replecs/server/registry.luau +30 -1
- package/out/replecs/shared.luau +3 -1
- package/out/replecs/types.luau +3 -0
- package/out/replecs/ver.luau +1 -1
- package/package.json +3 -2
|
@@ -891,20 +891,11 @@ local function process_entity_updates(
|
|
|
891
891
|
end
|
|
892
892
|
end
|
|
893
893
|
|
|
894
|
-
-- one update group: [u16 group mask,
|
|
895
|
-
--
|
|
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(
|
|
@@ -1533,8 +1532,8 @@ local function prune_component_serialization(self: Client, component: Component,
|
|
|
1533
1532
|
end
|
|
1534
1533
|
end
|
|
1535
1534
|
|
|
1536
|
-
-- serdes take part in the handshake
|
|
1537
|
-
-- local to each side and never
|
|
1535
|
+
-- serdes and preprocessors take part in the handshake and mark shared dirty,
|
|
1536
|
+
-- delta serializers are local to each side and never do
|
|
1538
1537
|
function client.set_serdes(self: Client, component: Component, serdes: types.Serdes)
|
|
1539
1538
|
ensure_component_serialization(self, component).serdes = serdes
|
|
1540
1539
|
self.is_shared_dirty = true
|
|
@@ -1552,6 +1551,7 @@ end
|
|
|
1552
1551
|
|
|
1553
1552
|
function client.set_preprocessor(self: Client, component: Component, preprocessor: types.Preprocessor)
|
|
1554
1553
|
ensure_component_serialization(self, component).preprocessor = preprocessor
|
|
1554
|
+
self.is_shared_dirty = true
|
|
1555
1555
|
end
|
|
1556
1556
|
|
|
1557
1557
|
function client.remove_preprocessor(self: Client, component: Component)
|
|
@@ -1561,6 +1561,7 @@ function client.remove_preprocessor(self: Client, component: Component)
|
|
|
1561
1561
|
end
|
|
1562
1562
|
options.preprocessor = nil
|
|
1563
1563
|
prune_component_serialization(self, component, options)
|
|
1564
|
+
self.is_shared_dirty = true
|
|
1564
1565
|
end
|
|
1565
1566
|
|
|
1566
1567
|
function client.set_delta_serializer(self: Client, component: Component, delta_serializer: types.DeltaSerializer)
|
|
@@ -1576,21 +1577,26 @@ function client.remove_delta_serializer(self: Client, component: Component)
|
|
|
1576
1577
|
prune_component_serialization(self, component, options)
|
|
1577
1578
|
end
|
|
1578
1579
|
|
|
1579
|
-
-- rebuilds the shared lookups (component ids, custom ids, serdes)
|
|
1580
|
-
-- flagged; cheap no-op otherwise, so every deserialize entry point calls it
|
|
1580
|
+
-- rebuilds the shared lookups (component ids, custom ids, serdes, preprocessors)
|
|
1581
|
+
-- when flagged; cheap no-op otherwise, so every deserialize entry point calls it
|
|
1581
1582
|
function client.resolve_dirty(self: Client)
|
|
1582
1583
|
if not self.is_shared_dirty then
|
|
1583
1584
|
return
|
|
1584
1585
|
end
|
|
1585
1586
|
|
|
1586
1587
|
local serdes: { [Component]: types.Serdes } = {}
|
|
1588
|
+
local preprocessors: { [Component]: true } = {}
|
|
1587
1589
|
for component, options in self.component_serialization do
|
|
1588
1590
|
if options.serdes then
|
|
1589
1591
|
serdes[component] = options.serdes
|
|
1590
1592
|
end
|
|
1593
|
+
if options.preprocessor then
|
|
1594
|
+
preprocessors[component] = true
|
|
1595
|
+
end
|
|
1591
1596
|
end
|
|
1592
1597
|
|
|
1593
|
-
self.shared =
|
|
1598
|
+
self.shared =
|
|
1599
|
+
shared.resolve_shared(self.world, self.components, self.registered_custom_ids, serdes, preprocessors)
|
|
1594
1600
|
self.is_shared_dirty = false
|
|
1595
1601
|
end
|
|
1596
1602
|
|
|
@@ -13,6 +13,7 @@ function handshake.generate_handshake(shared: types.Shared): types.HandshakeInfo
|
|
|
13
13
|
components = {},
|
|
14
14
|
custom_ids = {},
|
|
15
15
|
serdes = {},
|
|
16
|
+
preprocessors = {},
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
for component_name, component in shared.components.lookup do
|
|
@@ -36,6 +37,16 @@ function handshake.generate_handshake(shared: types.Shared): types.HandshakeInfo
|
|
|
36
37
|
}
|
|
37
38
|
end
|
|
38
39
|
|
|
40
|
+
-- preprocessors are functions, so only their presence is shared
|
|
41
|
+
for component in shared.preprocessors do
|
|
42
|
+
local name = component_to_name[component]
|
|
43
|
+
if not name then
|
|
44
|
+
continue
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
handshake.preprocessors[name] = true
|
|
48
|
+
end
|
|
49
|
+
|
|
39
50
|
return handshake
|
|
40
51
|
end
|
|
41
52
|
|
|
@@ -102,6 +113,30 @@ function handshake.verify_handshake(
|
|
|
102
113
|
end
|
|
103
114
|
end
|
|
104
115
|
|
|
116
|
+
for component_name in handshake.preprocessors do
|
|
117
|
+
local component = shared.components.lookup[component_name]
|
|
118
|
+
if not component then
|
|
119
|
+
common.log_warn(
|
|
120
|
+
`{handshake_side} registered a preprocessor for "{component_name}" that {verify_side} doesn't have`
|
|
121
|
+
)
|
|
122
|
+
continue
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if not shared.preprocessors[component] then
|
|
126
|
+
return false, `missing preprocessor for component "{component_name}" in {verify_side}`
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
for component in shared.preprocessors do
|
|
130
|
+
local component_name = component_to_name[component]
|
|
131
|
+
if not component_name then
|
|
132
|
+
continue
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if not handshake.preprocessors[component_name] then
|
|
136
|
+
return false, `missing preprocessor for component "{component_name}" in {handshake_side}`
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
105
140
|
return true, nil
|
|
106
141
|
end
|
|
107
142
|
|
package/out/replecs/index.d.ts
CHANGED
|
@@ -34,10 +34,42 @@ declare namespace Replecs {
|
|
|
34
34
|
| Player
|
|
35
35
|
| MemberFilterMap
|
|
36
36
|
| MemberFilterPredicate
|
|
37
|
+
| FilterNode
|
|
37
38
|
| Record<string, never>
|
|
38
39
|
| undefined;
|
|
39
40
|
type Member = unknown;
|
|
40
41
|
|
|
42
|
+
/** What a filter combinator accepts: another node or a raw player / set. */
|
|
43
|
+
export type FilterOperand = FilterNode | Player | MemberFilterMap;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One node of the reactive filter graph. Sources own a player set; derived
|
|
47
|
+
* nodes combine their inputs and re-evaluate lazily on the next collect.
|
|
48
|
+
* Structure is fixed at construction, only the value is reactive.
|
|
49
|
+
*/
|
|
50
|
+
export interface FilterNode {
|
|
51
|
+
/** Replaces the member set. A map of `false` values makes a blacklist. Sources only. */
|
|
52
|
+
set(filter: Player | MemberFilterMap): void;
|
|
53
|
+
/** Adds to the member set (on a blacklist source this excludes the player). Sources only. */
|
|
54
|
+
add(player: Player): void;
|
|
55
|
+
/** Sources only. */
|
|
56
|
+
remove(player: Player): void;
|
|
57
|
+
/** Sources only. */
|
|
58
|
+
clear(): void;
|
|
59
|
+
|
|
60
|
+
/** Players in this node and in every operand. */
|
|
61
|
+
filter(...operands: FilterOperand[]): FilterNode;
|
|
62
|
+
/** Players in this node or in any operand. */
|
|
63
|
+
extend(...operands: FilterOperand[]): FilterNode;
|
|
64
|
+
/** Players in this node and in no operand. */
|
|
65
|
+
exclude(...operands: FilterOperand[]): FilterNode;
|
|
66
|
+
|
|
67
|
+
/** Resolves the node and returns its current players. Errors if the node is not bound to a server yet. */
|
|
68
|
+
members(): Map<Player, true>;
|
|
69
|
+
/** Resolves the node and tests one player (aliases accepted). */
|
|
70
|
+
contains(player: Player): boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
41
73
|
export interface SharedInfo<T> {
|
|
42
74
|
lookup: Record<string, T>;
|
|
43
75
|
keys: string[];
|
|
@@ -64,6 +96,7 @@ declare namespace Replecs {
|
|
|
64
96
|
components: SharedInfo<Entity>;
|
|
65
97
|
custom_ids: SharedInfo<CustomId>;
|
|
66
98
|
serdes: Map<Id, SerdesTable>;
|
|
99
|
+
preprocessors: Map<Id, true>;
|
|
67
100
|
}
|
|
68
101
|
interface HandshakeSerdesInfo {
|
|
69
102
|
includes_variants?: boolean;
|
|
@@ -74,6 +107,7 @@ declare namespace Replecs {
|
|
|
74
107
|
components: Record<string, boolean>;
|
|
75
108
|
custom_ids: Record<string, boolean>;
|
|
76
109
|
serdes: Record<string, HandshakeSerdesInfo>;
|
|
110
|
+
preprocessors: Record<string, boolean>;
|
|
77
111
|
}
|
|
78
112
|
|
|
79
113
|
export interface Components {
|
|
@@ -194,6 +228,13 @@ declare namespace Replecs {
|
|
|
194
228
|
}
|
|
195
229
|
|
|
196
230
|
export interface ServerImp extends SerializationOptions {
|
|
231
|
+
/** A source filter bound to this server. */
|
|
232
|
+
filter(initial?: Player | MemberFilterMap): FilterNode;
|
|
233
|
+
/** A filter driven by a predicate over registered players, re-evaluated once per collect. */
|
|
234
|
+
filter_predicator(predicate: MemberFilterPredicate): FilterNode;
|
|
235
|
+
/** Every registered player; the base for blacklists. */
|
|
236
|
+
everyone(): FilterNode;
|
|
237
|
+
|
|
197
238
|
set_networked(entity: Entity, filter?: MemberFilter): void;
|
|
198
239
|
set_reliable(
|
|
199
240
|
entity: Entity,
|
|
@@ -289,6 +330,8 @@ declare namespace Replecs {
|
|
|
289
330
|
identifier: string,
|
|
290
331
|
handler?: (ctx: HandleContext) => Entity,
|
|
291
332
|
) => CustomId;
|
|
333
|
+
/** An unbound source filter; it attaches to a server the first time a `set_*` call receives it. */
|
|
334
|
+
filter: (initial?: Player | MemberFilterMap) => FilterNode;
|
|
292
335
|
}
|
|
293
336
|
}
|
|
294
337
|
|
package/out/replecs/init.luau
CHANGED
|
@@ -10,6 +10,7 @@ local VERSION = require (script:WaitForChild('ver'))
|
|
|
10
10
|
|
|
11
11
|
export type Server = server.Server
|
|
12
12
|
export type Client = client.Client
|
|
13
|
+
export type FilterNode = server.FilterNode
|
|
13
14
|
|
|
14
15
|
type Entity = common.Entity
|
|
15
16
|
type World = common.World
|
|
@@ -46,6 +47,7 @@ type Replecs = types.Components & {
|
|
|
46
47
|
create_server: (world: World?) -> Server,
|
|
47
48
|
create_client: (world: World?) -> Client,
|
|
48
49
|
create_custom_id: (identifier: string, handler: (ctx: HandleContext) -> Entity?) -> CustomId,
|
|
50
|
+
filter: (initial: types.FilterSimple?) -> FilterNode,
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
local replecs = {}
|
|
@@ -103,6 +105,12 @@ function replecs.create_custom_id(identifier: string, handler: (ctx: HandleConte
|
|
|
103
105
|
return customid.create(identifier, handler)
|
|
104
106
|
end
|
|
105
107
|
|
|
108
|
+
-- an unbound source filter: usable from any module, it attaches to the server
|
|
109
|
+
-- the first time a set_* call receives it (directly or through a derived node)
|
|
110
|
+
function replecs.filter(initial: types.FilterSimple?): FilterNode
|
|
111
|
+
return server.create_filter(initial)
|
|
112
|
+
end
|
|
113
|
+
|
|
106
114
|
function replecs.after_replication(lib: ReplecsLib, callback: () -> ())
|
|
107
115
|
local lib_client = lib.client
|
|
108
116
|
if lib_client then
|