@rbxts/replecs 0.3.1 → 0.4.0-rc.2

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.
@@ -0,0 +1,178 @@
1
+ --!optimize 2
2
+ local bitmasks = require (script.Parent:WaitForChild('bitmasks'))
3
+ local utils = require (script.Parent.Parent:WaitForChild('utils'))
4
+
5
+ type Set<T> = { [T]: boolean }
6
+ type Map<K, V> = { [K]: V }
7
+ type Array<T> = { T }
8
+ type Bitmask = buffer
9
+
10
+ type Client = any
11
+ export type MemberPredicator = (Client) -> boolean
12
+
13
+ type RegistryClass = {
14
+ client_indexes: Map<Client, number>,
15
+ -- reverse of client_indexes, kept in sync by register/unregister
16
+ index_clients: Map<number, Client>,
17
+
18
+ client_next_index: number,
19
+ client_count: number,
20
+
21
+ -- indexes released by unregister, reused before growing client_next_index
22
+ free_indexes: Array<number>,
23
+
24
+ -- alternate keys a filter may use for a client (a user id, a name...);
25
+ -- resolved to the client before registration is considered
26
+ client_aliases: Map<any, Client>,
27
+
28
+ -- hard cap on registered clients: every bitmask is allocated to hold
29
+ -- exactly this many bits, so an index past it would fall off the buffer
30
+ max_clients: number,
31
+ bitmask_size: number,
32
+ }
33
+
34
+ local registry = {}
35
+ registry.__index = registry
36
+
37
+ export type Registry = RegistryClass & typeof(registry)
38
+
39
+ -- a filter can name a player before it joined the registry: register it on
40
+ -- the spot so the bit is stable once the player is ready. a player that
41
+ -- already left is never registered, its bit would leak into every mask
42
+ local function resolve_member(self: Registry, member: Client): number?
43
+ local index = self.client_indexes[member]
44
+ if index then
45
+ return index
46
+ end
47
+
48
+ -- an alias stands for its client: registered or not, the client is what
49
+ -- gets the bit, the alias itself is never registered
50
+ local aliased = self.client_aliases[member]
51
+ if aliased ~= nil then
52
+ member = aliased
53
+ index = self.client_indexes[member]
54
+ if index then
55
+ return index
56
+ end
57
+ end
58
+
59
+ if not utils.is_client_valid(member) then
60
+ return nil
61
+ end
62
+
63
+ registry.register_client(self, member)
64
+ return self.client_indexes[member]
65
+ end
66
+
67
+ function registry.add_alias(self: Registry, client: Client, alias: any)
68
+ self.client_aliases[alias] = client
69
+ end
70
+
71
+ function registry.remove_alias(self: Registry, alias: any)
72
+ self.client_aliases[alias] = nil
73
+ end
74
+
75
+ -- drops every alias pointing at the client
76
+ function registry.remove_client_aliases(self: Registry, client: Client)
77
+ local aliases = self.client_aliases
78
+ for alias, aliased in aliases do
79
+ if aliased == client then
80
+ aliases[alias] = nil
81
+ end
82
+ end
83
+ end
84
+
85
+
86
+ function registry.create_bitmask_from_set(self: Registry, member_set: Map<Client, boolean>): Bitmask
87
+ local resolved_members: Array<number> = {}
88
+ for member in member_set do
89
+ local resolved = resolve_member(self, member)
90
+ if resolved then
91
+ table.insert(resolved_members, resolved)
92
+ end
93
+ end
94
+
95
+ local bitmask = buffer.create(self.bitmask_size)
96
+
97
+ for _, index in resolved_members do
98
+ bitmasks.set(bitmask, index)
99
+ end
100
+
101
+ return bitmask
102
+ end
103
+
104
+
105
+ function registry.create_bitmask_from_member(self: Registry, member: Client): Bitmask
106
+ local bitmask = buffer.create(self.bitmask_size)
107
+ local index = resolve_member(self, member)
108
+ if index then
109
+ bitmasks.set(bitmask, index)
110
+ end
111
+ return bitmask
112
+ end
113
+
114
+
115
+ function registry.create_bitmask_from_function(self: Registry, predicator: MemberPredicator)
116
+ local bitmask = buffer.create(self.bitmask_size)
117
+
118
+ for member, index in self.client_indexes do
119
+ if predicator(member) then
120
+ bitmasks.set(bitmask, index)
121
+ end
122
+ end
123
+
124
+ return bitmask
125
+ end
126
+
127
+ function registry.register_client(self: Registry, member: Client)
128
+ if self.client_indexes[member] then
129
+ return
130
+ end
131
+ if self.client_count >= self.max_clients then
132
+ error(`client limit of {self.max_clients} reached, raise MAX_CLIENTS to register more`)
133
+ end
134
+
135
+ local reused = table.remove(self.free_indexes)
136
+ local index: number
137
+ if reused then
138
+ index = reused
139
+ else
140
+ index = self.client_next_index
141
+ self.client_next_index += 1
142
+ end
143
+ self.client_indexes[member] = index
144
+ self.index_clients[index] = member
145
+ self.client_count += 1
146
+ end
147
+
148
+ function registry.unregister_client(self: Registry, member: Client)
149
+ local index = self.client_indexes[member]
150
+ if not index then
151
+ return
152
+ end
153
+
154
+ table.insert(self.free_indexes, index)
155
+ self.client_indexes[member] = nil
156
+ self.index_clients[index] = nil
157
+ self.client_count -= 1
158
+ end
159
+
160
+ local DEFAULT_MAX_CLIENTS = 128
161
+
162
+ function registry.create(max_clients: number?): Registry
163
+ local self = {} :: Registry
164
+
165
+ local cap = max_clients or DEFAULT_MAX_CLIENTS
166
+ self.max_clients = cap
167
+ self.bitmask_size = bitmasks.get_buffer_size(cap)
168
+ self.client_indexes = {}
169
+ self.index_clients = {}
170
+ self.client_next_index = 0
171
+ self.client_count = 0
172
+ self.free_indexes = {}
173
+ self.client_aliases = {}
174
+
175
+ return setmetatable(self, registry) :: any
176
+ end
177
+
178
+ return registry
@@ -0,0 +1,20 @@
1
+ type Set<T> = { [T]: boolean }
2
+
3
+ local utils = {}
4
+
5
+ function utils.is_include_filter(filter: Set<any>): boolean
6
+ for _, allow in filter do
7
+ return allow
8
+ end
9
+ return true
10
+ end
11
+
12
+ function utils.get_used_filter_type(filter: any)
13
+ if typeof(filter) == "table" then
14
+ return utils.is_include_filter(filter)
15
+ else
16
+ return true
17
+ end
18
+ end
19
+
20
+ return utils
@@ -0,0 +1,70 @@
1
+ local types = require (script.Parent:WaitForChild('types'))
2
+ local common = require (script.Parent:WaitForChild('common'))
3
+ local customid = require (script.Parent:WaitForChild('customid'))
4
+
5
+ type Component = common.Component
6
+ type Entity = common.Entity
7
+ type World = common.World
8
+
9
+ local ECS_NAME = common.ecs_name
10
+
11
+ local shared = {}
12
+
13
+ local function create_shared_lookup<T>(lookup: { [string]: T }): types.SharedInfo<T>
14
+ local keys = {}
15
+ local indexes = {}
16
+ local members = {}
17
+
18
+ for name in lookup do
19
+ table.insert(keys, name)
20
+ end
21
+ table.sort(keys)
22
+
23
+ for index, name in keys do
24
+ local member = lookup[name]
25
+ members[member] = index
26
+ indexes[index] = member
27
+ end
28
+
29
+ return {
30
+ lookup = lookup,
31
+ keys = keys,
32
+ indexes = indexes,
33
+ members = members,
34
+ }
35
+ end
36
+
37
+ function shared.resolve_shared(
38
+ world: World,
39
+ components: types.Components,
40
+ registered_custom_ids: { [customid.CustomId]: boolean },
41
+ serdes: { [Component]: types.Serdes }
42
+ ): types.Shared
43
+ local shared = {} :: types.Shared
44
+
45
+ local components_lookup: { [string]: Entity } = table.clone(common.implicitly_shared)
46
+ local custom_ids_lookup = {}
47
+
48
+ for component, name in world:query(ECS_NAME):with(components.shared):iter() do
49
+ if components_lookup[name] then
50
+ common.log_error(`detected two components with the same {name} name: ({components_lookup[name]}, {component})`)
51
+ end
52
+ components_lookup[name] = component
53
+ end
54
+
55
+ for component in world:query(components.shared):without(ECS_NAME):iter() do
56
+ common.log_warn(`shared component: {component} has no name assigned.`)
57
+ end
58
+
59
+ for custom in registered_custom_ids do
60
+ custom_ids_lookup[custom.identifier] = custom
61
+ end
62
+
63
+ shared.serdes = serdes
64
+ shared.components = create_shared_lookup(components_lookup)
65
+ shared.custom_ids = create_shared_lookup(custom_ids_lookup) :: types.SharedInfo<customid.CustomId>
66
+
67
+ return shared
68
+ end
69
+
70
+ return shared
@@ -1,23 +1,27 @@
1
1
  local customid = require (script.Parent:WaitForChild('customid'))
2
2
  local common = require (script.Parent:WaitForChild('common'))
3
3
 
4
- export type PlayerFilter = {
5
- [Player]: boolean,
6
- }
7
-
8
- export type FunctionFilter = (player: Player) -> boolean
9
- export type AnyFilter = PlayerFilter | Player
10
-
11
4
  type Entity = common.Entity
12
5
  type World = common.World
13
6
  type Component<T = any> = common.Component<T>
14
7
 
8
+ type Player = any
9
+
10
+ export type FilterSet = {
11
+ [Player]: boolean,
12
+ }
13
+ export type FilterPlayer = Player
14
+ export type FilterPredicator = (Player) -> boolean
15
+ export type FilterAny = FilterPlayer | FilterSet | FilterPredicator
16
+ export type FilterSimple = FilterPlayer | FilterSet
17
+
15
18
  export type SharedInfo<T> = {
16
19
  lookup: { [string]: T },
17
20
  keys: { string },
18
21
  indexes: { [number]: T },
19
22
  members: { [T]: number },
20
23
  }
24
+
21
25
  export type HandshakeInfo = {
22
26
  components: { [string]: boolean },
23
27
  custom_ids: { [string]: boolean },
@@ -34,17 +38,30 @@ export type Serdes<T = any> = {
34
38
  deserialize: (buffer,...{ any }) -> T,
35
39
  }
36
40
 
41
+ export type Preprocessor<B = any, A = any> = {
42
+ serialize: (B) -> A,
43
+ deserialize: (A) -> B,
44
+ }
45
+
46
+ export type DeltaSerializer<T = any, D = T> = {
47
+ snapshot: ((value: T) -> T)?,
48
+ delta: (old: T, curr: T) -> D,
49
+ apply: (curr: T, delta: D) -> T,
50
+ }
51
+
37
52
  export type Components = {
38
53
  shared: Entity,
39
54
  Shared: Entity,
40
55
  networked: Component<any?>,
41
- Networked: Component<AnyFilter?>,
42
- reliable: Component<AnyFilter?>,
43
- Reliable: Component<AnyFilter?>,
44
- unreliable: Component<AnyFilter?>,
45
- Unreliable: Component<AnyFilter?>,
46
- relation: Component<AnyFilter?>,
47
- Relation: Component<AnyFilter?>,
56
+ Networked: Component<FilterAny?>,
57
+ reliable: Component<FilterAny?>,
58
+ Reliable: Component<FilterAny?>,
59
+ unreliable: Component<FilterAny?>,
60
+ Unreliable: Component<FilterAny?>,
61
+ once: Component<FilterAny?>,
62
+ Once: Component<FilterAny?>,
63
+ relation: Component<FilterAny?>,
64
+ Relation: Component<FilterAny?>,
48
65
 
49
66
  serdes: Component<Serdes>,
50
67
  Serdes: Component<Serdes>,
@@ -52,8 +69,6 @@ export type Components = {
52
69
  Custom: Entity,
53
70
  custom_handler: Component<(val: any) -> Entity?>,
54
71
  CustomHandler: Component<(val: any) -> Entity?>,
55
- global: Component<number>,
56
- Global: Component<number>,
57
72
  __alive_tracking__: Entity,
58
73
  }
59
74