@axpecter/lync 1.4.3 → 1.5.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/README.md +41 -2
- package/package.json +1 -1
- package/src/Types.luau +13 -1
- package/src/api/Group.luau +35 -28
- package/src/api/Namespace.luau +107 -89
- package/src/api/Packet.luau +69 -51
- package/src/api/Query.luau +101 -95
- package/src/api/Scope.luau +32 -18
- package/src/api/Signal.luau +78 -56
- package/src/codec/Base.luau +17 -17
- package/src/codec/composite/Array.luau +74 -74
- package/src/codec/composite/Map.luau +80 -80
- package/src/codec/composite/Optional.luau +14 -14
- package/src/codec/composite/Shared.luau +35 -35
- package/src/codec/composite/Struct.luau +108 -108
- package/src/codec/composite/Tagged.luau +71 -71
- package/src/codec/composite/Tuple.luau +35 -35
- package/src/codec/datatype/Buffer.luau +18 -18
- package/src/codec/datatype/CFrame.luau +24 -24
- package/src/codec/datatype/Color.luau +8 -12
- package/src/codec/datatype/Instance.luau +13 -13
- package/src/codec/datatype/IntVector.luau +17 -17
- package/src/codec/datatype/NumberRange.luau +7 -7
- package/src/codec/datatype/Ray.luau +16 -16
- package/src/codec/datatype/Rect.luau +13 -13
- package/src/codec/datatype/Region.luau +32 -36
- package/src/codec/datatype/Sequence.luau +41 -41
- package/src/codec/datatype/String.luau +28 -28
- package/src/codec/datatype/UDim.luau +17 -17
- package/src/codec/datatype/Vector.luau +14 -18
- package/src/codec/meta/Auto.luau +75 -73
- package/src/codec/meta/Bitfield.luau +46 -46
- package/src/codec/meta/Custom.luau +7 -7
- package/src/codec/meta/Enum.luau +25 -25
- package/src/codec/meta/Nothing.luau +3 -3
- package/src/codec/meta/Quantized.luau +63 -60
- package/src/codec/meta/Unknown.luau +11 -11
- package/src/codec/primitive/Bool.luau +12 -12
- package/src/codec/primitive/Float16.luau +28 -28
- package/src/codec/primitive/Number.luau +41 -41
- package/src/codec/primitive/Varint.luau +19 -19
- package/src/init.luau +81 -61
- package/src/internal/Baseline.luau +7 -7
- package/src/internal/Channel.luau +56 -56
- package/src/internal/Middleware.luau +29 -29
- package/src/internal/Pool.luau +15 -15
- package/src/internal/Registry.luau +19 -19
- package/src/transport/Bridge.luau +17 -17
- package/src/transport/Client.luau +46 -46
- package/src/transport/Gate.luau +56 -64
- package/src/transport/Reader.luau +34 -34
- package/src/transport/Server.luau +89 -89
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
```toml
|
|
15
15
|
[dependencies]
|
|
16
|
-
Lync = "axp3cter/lync@1.
|
|
16
|
+
Lync = "axp3cter/lync@1.5.0"
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
**npm (roblox-ts)**
|
|
@@ -163,7 +163,7 @@ end
|
|
|
163
163
|
| | What it does |
|
|
164
164
|
|:---------|:------------|
|
|
165
165
|
| `Lync.start()` | Sets up transport. Server creates remotes, client connects. Call once after all definitions. |
|
|
166
|
-
| `Lync.VERSION` | `"1.
|
|
166
|
+
| `Lync.VERSION` | `"1.5.0"` |
|
|
167
167
|
|
|
168
168
|
## Packets
|
|
169
169
|
|
|
@@ -228,6 +228,45 @@ packet:send(data) -- send to server
|
|
|
228
228
|
|
|
229
229
|
`Lync.defineNamespace(name, config)` returns a Namespace. Takes a `packets` table and/or a `queries` table. All names get auto-prefixed with `"YourNamespace."` so nothing collides.
|
|
230
230
|
|
|
231
|
+
The config takes `PacketConfig` and `QueryConfig` objects (same shape you'd pass to `definePacket` / `defineQuery`). The namespace creates and owns the packets/queries internally.
|
|
232
|
+
|
|
233
|
+
```luau
|
|
234
|
+
local Combat = Lync.defineNamespace("Combat", {
|
|
235
|
+
packets = {
|
|
236
|
+
Hit = {
|
|
237
|
+
value = Lync.struct({ targetId = Lync.u16, damage = Lync.f32, headshot = Lync.bool }),
|
|
238
|
+
rateLimit = { maxPerSecond = 30, burstAllowance = 5 },
|
|
239
|
+
validate = function(data, player)
|
|
240
|
+
if data.damage > 200 then return false, "damage" end
|
|
241
|
+
return true
|
|
242
|
+
end,
|
|
243
|
+
},
|
|
244
|
+
Death = { value = Lync.u16 },
|
|
245
|
+
},
|
|
246
|
+
queries = {
|
|
247
|
+
Stats = {
|
|
248
|
+
request = Lync.nothing,
|
|
249
|
+
response = Lync.struct({ kills = Lync.u16, deaths = Lync.u16 }),
|
|
250
|
+
timeout = 3,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
-- Access by short name directly on the namespace
|
|
256
|
+
Combat.Hit:send(data, player)
|
|
257
|
+
Combat.Death:listen(function(targetId, sender) end)
|
|
258
|
+
Combat.Stats:listen(function(request, player) return { kills = 10, deaths = 2 } end)
|
|
259
|
+
|
|
260
|
+
-- Or via the typed sub-tables
|
|
261
|
+
Combat.packets.Hit:send(data, player)
|
|
262
|
+
Combat.queries.Stats:request(nil)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
| Config field | Type | What it does |
|
|
266
|
+
|:-------------|:-----|:-------------|
|
|
267
|
+
| `packets` | `{ [string]: PacketConfig }?` | Map of short name → packet config. Each entry becomes a Packet on the namespace. |
|
|
268
|
+
| `queries` | `{ [string]: QueryConfig }?` | Map of short name → query config. Each entry becomes a Query on the namespace. |
|
|
269
|
+
|
|
231
270
|
Access packets and queries by their short name on the returned object: `ns.PacketName`, `ns.QueryName`. Or use the typed sub-tables: `ns.packets.PacketName`, `ns.queries.QueryName`.
|
|
232
271
|
|
|
233
272
|
| Method | What it does |
|
package/package.json
CHANGED
package/src/Types.luau
CHANGED
|
@@ -51,6 +51,18 @@ export type QueryConfig<Req, Resp> = {
|
|
|
51
51
|
validate: ((data: Req, player: Player) -> (boolean, string?))?,
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
export type NamespaceConfig = {
|
|
55
|
+
packets: { [string]: PacketConfig<any> }?,
|
|
56
|
+
queries: { [string]: QueryConfig<any, any> }?,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
-- Minimal signal interface for Registration. Avoids circular dependency
|
|
60
|
+
-- with Signal.luau while typing the two methods consumers actually call.
|
|
61
|
+
export type SignalLike = {
|
|
62
|
+
fire: (self: SignalLike, ...any) -> (),
|
|
63
|
+
connect: (self: SignalLike, callback: (...any) -> ()) -> Connection,
|
|
64
|
+
}
|
|
65
|
+
|
|
54
66
|
export type Registration = {
|
|
55
67
|
id: number,
|
|
56
68
|
name: string,
|
|
@@ -59,7 +71,7 @@ export type Registration = {
|
|
|
59
71
|
isUnreliable: boolean,
|
|
60
72
|
kind: number,
|
|
61
73
|
partner: number?,
|
|
62
|
-
signal:
|
|
74
|
+
signal: SignalLike,
|
|
63
75
|
rateLimit: RateLimitConfig?,
|
|
64
76
|
validate: ((data: any, player: Player) -> (boolean, string?))?,
|
|
65
77
|
needsGate: boolean,
|
package/src/api/Group.luau
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
--!optimize 2
|
|
3
3
|
-- Named player sets with auto-cleanup on PlayerRemoving.
|
|
4
4
|
|
|
5
|
-
local Players = game:GetService
|
|
5
|
+
local Players = game:GetService("Players")
|
|
6
6
|
|
|
7
|
-
-- State
|
|
7
|
+
-- State ---------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local _groups = {} :: { [string]: { [Player]: true } }
|
|
10
10
|
local _counts = {} :: { [string]: number }
|
|
11
11
|
local _playerGroups = {} :: { [Player]: { [string]: true } }
|
|
12
12
|
|
|
13
|
-
-- Private
|
|
13
|
+
-- Private -------------------------------------------------------------
|
|
14
14
|
|
|
15
|
-
local function onPlayerRemoving
|
|
15
|
+
local function onPlayerRemoving(player: Player): ()
|
|
16
16
|
local memberships = _playerGroups[player]
|
|
17
17
|
if not memberships then
|
|
18
18
|
return
|
|
@@ -29,16 +29,23 @@ local function onPlayerRemoving (player: Player): ()
|
|
|
29
29
|
_playerGroups[player] = nil
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
Players.PlayerRemoving:Connect
|
|
32
|
+
Players.PlayerRemoving:Connect(onPlayerRemoving)
|
|
33
|
+
|
|
34
|
+
type GroupFields = {
|
|
35
|
+
_tag: "group",
|
|
36
|
+
_name: string,
|
|
37
|
+
}
|
|
33
38
|
|
|
34
39
|
local GroupImpl = {}
|
|
35
40
|
GroupImpl.__index = GroupImpl
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
export type Group = typeof(setmetatable({} :: GroupFields, GroupImpl))
|
|
43
|
+
|
|
44
|
+
function GroupImpl.add(self: Group, player: Player): boolean
|
|
38
45
|
local name = self._name
|
|
39
46
|
local set = _groups[name]
|
|
40
47
|
if not set then
|
|
41
|
-
error
|
|
48
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
if set[player] then
|
|
@@ -58,11 +65,11 @@ function GroupImpl.add (self: any, player: Player): boolean
|
|
|
58
65
|
return true
|
|
59
66
|
end
|
|
60
67
|
|
|
61
|
-
function GroupImpl.remove
|
|
68
|
+
function GroupImpl.remove(self: Group, player: Player): boolean
|
|
62
69
|
local name = self._name
|
|
63
70
|
local set = _groups[name]
|
|
64
71
|
if not set then
|
|
65
|
-
error
|
|
72
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
66
73
|
end
|
|
67
74
|
|
|
68
75
|
if not set[player] then
|
|
@@ -80,48 +87,48 @@ function GroupImpl.remove (self: any, player: Player): boolean
|
|
|
80
87
|
return true
|
|
81
88
|
end
|
|
82
89
|
|
|
83
|
-
function GroupImpl.has
|
|
90
|
+
function GroupImpl.has(self: Group, player: Player): boolean
|
|
84
91
|
local name = self._name
|
|
85
92
|
local set = _groups[name]
|
|
86
93
|
if not set then
|
|
87
|
-
error
|
|
94
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
88
95
|
end
|
|
89
96
|
return set[player] == true
|
|
90
97
|
end
|
|
91
98
|
|
|
92
|
-
function GroupImpl.count
|
|
99
|
+
function GroupImpl.count(self: Group): number
|
|
93
100
|
local name = self._name
|
|
94
101
|
if not _groups[name] then
|
|
95
|
-
error
|
|
102
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
96
103
|
end
|
|
97
104
|
return _counts[name]
|
|
98
105
|
end
|
|
99
106
|
|
|
100
|
-
function GroupImpl.forEach
|
|
107
|
+
function GroupImpl.forEach(self: Group, fn: (player: Player) -> ()): ()
|
|
101
108
|
local name = self._name
|
|
102
109
|
local set = _groups[name]
|
|
103
110
|
if not set then
|
|
104
|
-
error
|
|
111
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
105
112
|
end
|
|
106
113
|
for player in set do
|
|
107
|
-
fn
|
|
114
|
+
fn(player)
|
|
108
115
|
end
|
|
109
116
|
end
|
|
110
117
|
|
|
111
|
-
function GroupImpl.getSet
|
|
118
|
+
function GroupImpl.getSet(self: Group): { [Player]: true }
|
|
112
119
|
local name = self._name
|
|
113
120
|
local set = _groups[name]
|
|
114
121
|
if not set then
|
|
115
|
-
error
|
|
122
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
116
123
|
end
|
|
117
124
|
return set
|
|
118
125
|
end
|
|
119
126
|
|
|
120
|
-
function GroupImpl.destroy
|
|
127
|
+
function GroupImpl.destroy(self: Group): ()
|
|
121
128
|
local name = self._name
|
|
122
129
|
local set = _groups[name]
|
|
123
130
|
if not set then
|
|
124
|
-
error
|
|
131
|
+
error(`[Lync] Group has been destroyed: "{name}"`)
|
|
125
132
|
end
|
|
126
133
|
|
|
127
134
|
for player in set do
|
|
@@ -135,23 +142,23 @@ function GroupImpl.destroy (self: any): ()
|
|
|
135
142
|
_counts[name] = nil
|
|
136
143
|
end
|
|
137
144
|
|
|
138
|
-
table.freeze
|
|
145
|
+
table.freeze(GroupImpl)
|
|
139
146
|
|
|
140
|
-
-- Public
|
|
147
|
+
-- Public --------------------------------------------------------------
|
|
141
148
|
|
|
142
|
-
local
|
|
149
|
+
local GroupModule = {}
|
|
143
150
|
|
|
144
|
-
function
|
|
151
|
+
function GroupModule.create(name: string): Group
|
|
145
152
|
if _groups[name] then
|
|
146
|
-
error
|
|
153
|
+
error(`[Lync] Group already exists: "{name}"`)
|
|
147
154
|
end
|
|
148
155
|
_groups[name] = {}
|
|
149
156
|
_counts[name] = 0
|
|
150
157
|
|
|
151
|
-
return setmetatable
|
|
152
|
-
_tag = "group",
|
|
158
|
+
return setmetatable({
|
|
159
|
+
_tag = "group" :: "group",
|
|
153
160
|
_name = name,
|
|
154
161
|
}, GroupImpl)
|
|
155
162
|
end
|
|
156
163
|
|
|
157
|
-
return table.freeze
|
|
164
|
+
return table.freeze(GroupModule)
|
package/src/api/Namespace.luau
CHANGED
|
@@ -2,57 +2,66 @@
|
|
|
2
2
|
--!optimize 2
|
|
3
3
|
-- Scoped packet/query groups with batch ops and local middleware.
|
|
4
4
|
|
|
5
|
-
local Middleware = require
|
|
6
|
-
local Packet = require
|
|
7
|
-
local Query = require
|
|
8
|
-
local Types = require
|
|
5
|
+
local Middleware = require(script.Parent.Parent.internal.Middleware)
|
|
6
|
+
local Packet = require(script.Parent.Packet)
|
|
7
|
+
local Query = require(script.Parent.Query)
|
|
8
|
+
local Types = require(script.Parent.Parent.Types)
|
|
9
9
|
|
|
10
10
|
type Connection = Types.Connection
|
|
11
11
|
type PacketConfig<T> = Types.PacketConfig<T>
|
|
12
12
|
type QueryConfig<R, S> = Types.QueryConfig<R, S>
|
|
13
13
|
|
|
14
|
-
-- Constants
|
|
14
|
+
-- Constants -----------------------------------------------------------
|
|
15
15
|
|
|
16
16
|
local MAX_NAMESPACES = 64
|
|
17
17
|
|
|
18
|
-
-- State
|
|
18
|
+
-- State ---------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
local _registered = {} :: { [string]: true }
|
|
21
21
|
local _count = 0
|
|
22
22
|
|
|
23
|
-
-- Private
|
|
23
|
+
-- Private -------------------------------------------------------------
|
|
24
24
|
|
|
25
|
-
type NamespaceConfig =
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
type NamespaceConfig = Types.NamespaceConfig
|
|
26
|
+
|
|
27
|
+
type NamespaceFields = {
|
|
28
|
+
_name: string,
|
|
29
|
+
_packets: { [string]: Packet.Packet },
|
|
30
|
+
_queries: { [string]: Query.Query },
|
|
31
|
+
_tracked: { Connection },
|
|
32
|
+
_removers: { () -> () },
|
|
33
|
+
packets: { [string]: Packet.Packet },
|
|
34
|
+
queries: { [string]: Query.Query },
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
local function prefixName
|
|
37
|
+
local function prefixName(nsName: string, name: string): string
|
|
31
38
|
return `{nsName}.{name}`
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
-- Wraps a middleware handler so it only fires for packets in this namespace.
|
|
35
|
-
local function scopedHandler
|
|
42
|
+
local function scopedHandler(
|
|
36
43
|
nsName: string,
|
|
37
44
|
handler: (data: any, name: string, player: Player?) -> any?
|
|
38
45
|
): (data: any, name: string, player: Player?) -> any?
|
|
39
46
|
local prefix = nsName .. "."
|
|
40
47
|
local prefixLen = #prefix
|
|
41
48
|
|
|
42
|
-
return function
|
|
43
|
-
if string.sub
|
|
49
|
+
return function(data: any, name: string, player: Player?): any?
|
|
50
|
+
if string.sub(name, 1, prefixLen) ~= prefix then
|
|
44
51
|
return data
|
|
45
52
|
end
|
|
46
|
-
return handler
|
|
53
|
+
return handler(data, name, player)
|
|
47
54
|
end
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
local NamespaceImpl = {}
|
|
51
58
|
NamespaceImpl.__index = NamespaceImpl
|
|
52
59
|
|
|
60
|
+
export type Namespace = typeof(setmetatable({} :: NamespaceFields, NamespaceImpl))
|
|
61
|
+
|
|
53
62
|
-- Listens on every packet in this namespace. Returns a Connection.
|
|
54
|
-
function NamespaceImpl.listenAll
|
|
55
|
-
self:
|
|
63
|
+
function NamespaceImpl.listenAll(
|
|
64
|
+
self: Namespace,
|
|
56
65
|
callback: (name: string, data: any, sender: Player?) -> ()
|
|
57
66
|
): Connection
|
|
58
67
|
local connections = {} :: { Connection }
|
|
@@ -60,174 +69,183 @@ function NamespaceImpl.listenAll (
|
|
|
60
69
|
local prefixLen = #prefix
|
|
61
70
|
|
|
62
71
|
for fullName, packet in self._packets do
|
|
63
|
-
local shortName = string.sub
|
|
64
|
-
local conn = packet:listen
|
|
65
|
-
callback
|
|
72
|
+
local shortName = string.sub(fullName, prefixLen + 1)
|
|
73
|
+
local conn = packet:listen(function(data: any, sender: Player?): ()
|
|
74
|
+
callback(shortName, data, sender)
|
|
66
75
|
end)
|
|
67
|
-
table.insert
|
|
68
|
-
table.insert
|
|
76
|
+
table.insert(connections, conn)
|
|
77
|
+
table.insert(self._tracked, conn)
|
|
69
78
|
end
|
|
70
79
|
|
|
71
|
-
local bundle
|
|
80
|
+
local bundle: Connection
|
|
81
|
+
bundle = {
|
|
72
82
|
connected = true,
|
|
73
|
-
disconnect = function
|
|
74
|
-
if not
|
|
83
|
+
disconnect = function(self: Connection): ()
|
|
84
|
+
if not self.connected then
|
|
75
85
|
return
|
|
76
86
|
end
|
|
77
|
-
|
|
87
|
+
self.connected = false
|
|
78
88
|
|
|
79
89
|
for _, inner in connections do
|
|
80
90
|
if inner.connected then
|
|
81
|
-
inner:disconnect
|
|
91
|
+
inner:disconnect()
|
|
82
92
|
end
|
|
83
93
|
end
|
|
84
94
|
end,
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
return bundle
|
|
97
|
+
return bundle
|
|
88
98
|
end
|
|
89
99
|
|
|
90
100
|
-- Disconnects every listenAll connection. Does not remove scoped middleware.
|
|
91
|
-
function NamespaceImpl.disconnectAll
|
|
101
|
+
function NamespaceImpl.disconnectAll(self: Namespace): ()
|
|
92
102
|
for _, conn in self._tracked do
|
|
93
103
|
if conn.connected then
|
|
94
|
-
conn:disconnect
|
|
104
|
+
conn:disconnect()
|
|
95
105
|
end
|
|
96
106
|
end
|
|
97
|
-
table.clear
|
|
107
|
+
table.clear(self._tracked)
|
|
98
108
|
end
|
|
99
109
|
|
|
100
110
|
-- Registers send middleware scoped to this namespace. Returns a remover.
|
|
101
|
-
function NamespaceImpl.onSend
|
|
102
|
-
self:
|
|
111
|
+
function NamespaceImpl.onSend(
|
|
112
|
+
self: Namespace,
|
|
103
113
|
handler: (data: any, name: string, player: Player?) -> any?
|
|
104
114
|
): () -> ()
|
|
105
|
-
local wrapped = scopedHandler
|
|
106
|
-
local remover = Middleware.addSend
|
|
107
|
-
table.insert
|
|
115
|
+
local wrapped = scopedHandler(self._name, handler)
|
|
116
|
+
local remover = Middleware.addSend(wrapped)
|
|
117
|
+
table.insert(self._removers, remover)
|
|
108
118
|
|
|
109
|
-
return function
|
|
110
|
-
remover
|
|
111
|
-
local idx = table.find
|
|
119
|
+
return function(): ()
|
|
120
|
+
remover()
|
|
121
|
+
local idx = table.find(self._removers, remover)
|
|
112
122
|
if idx then
|
|
113
|
-
table.remove
|
|
123
|
+
table.remove(self._removers, idx)
|
|
114
124
|
end
|
|
115
125
|
end
|
|
116
126
|
end
|
|
117
127
|
|
|
118
128
|
-- Registers receive middleware scoped to this namespace. Returns a remover.
|
|
119
|
-
function NamespaceImpl.onReceive
|
|
120
|
-
self:
|
|
129
|
+
function NamespaceImpl.onReceive(
|
|
130
|
+
self: Namespace,
|
|
121
131
|
handler: (data: any, name: string, player: Player?) -> any?
|
|
122
132
|
): () -> ()
|
|
123
|
-
local wrapped = scopedHandler
|
|
124
|
-
local remover = Middleware.addReceive
|
|
125
|
-
table.insert
|
|
133
|
+
local wrapped = scopedHandler(self._name, handler)
|
|
134
|
+
local remover = Middleware.addReceive(wrapped)
|
|
135
|
+
table.insert(self._removers, remover)
|
|
126
136
|
|
|
127
|
-
return function
|
|
128
|
-
remover
|
|
129
|
-
local idx = table.find
|
|
137
|
+
return function(): ()
|
|
138
|
+
remover()
|
|
139
|
+
local idx = table.find(self._removers, remover)
|
|
130
140
|
if idx then
|
|
131
|
-
table.remove
|
|
141
|
+
table.remove(self._removers, idx)
|
|
132
142
|
end
|
|
133
143
|
end
|
|
134
144
|
end
|
|
135
145
|
|
|
136
146
|
-- Kills all listeners and removes all scoped middleware. Full cleanup.
|
|
137
|
-
function NamespaceImpl.destroy
|
|
138
|
-
self:disconnectAll
|
|
147
|
+
function NamespaceImpl.destroy(self: Namespace): ()
|
|
148
|
+
self:disconnectAll()
|
|
139
149
|
|
|
140
150
|
for _, remover in self._removers do
|
|
141
|
-
remover
|
|
151
|
+
remover()
|
|
142
152
|
end
|
|
143
|
-
table.clear
|
|
153
|
+
table.clear(self._removers)
|
|
144
154
|
end
|
|
145
155
|
|
|
146
|
-
-- Returns every packet name
|
|
147
|
-
function NamespaceImpl.packetNames
|
|
156
|
+
-- Returns every packet name(without prefix) in this namespace.
|
|
157
|
+
function NamespaceImpl.packetNames(self: Namespace): { string }
|
|
148
158
|
local result = {} :: { string }
|
|
149
159
|
local prefix = self._name .. "."
|
|
150
160
|
local prefixLen = #prefix
|
|
151
161
|
|
|
152
162
|
for fullName in self._packets do
|
|
153
|
-
table.insert
|
|
163
|
+
table.insert(result, string.sub(fullName, prefixLen + 1))
|
|
154
164
|
end
|
|
155
165
|
|
|
156
|
-
table.sort
|
|
166
|
+
table.sort(result)
|
|
157
167
|
return result
|
|
158
168
|
end
|
|
159
169
|
|
|
160
|
-
-- Returns every query name
|
|
161
|
-
function NamespaceImpl.queryNames
|
|
170
|
+
-- Returns every query name(without prefix) in this namespace.
|
|
171
|
+
function NamespaceImpl.queryNames(self: Namespace): { string }
|
|
162
172
|
local result = {} :: { string }
|
|
163
173
|
local prefix = self._name .. "."
|
|
164
174
|
local prefixLen = #prefix
|
|
165
175
|
|
|
166
176
|
for fullName in self._queries do
|
|
167
|
-
table.insert
|
|
177
|
+
table.insert(result, string.sub(fullName, prefixLen + 1))
|
|
168
178
|
end
|
|
169
179
|
|
|
170
|
-
table.sort
|
|
180
|
+
table.sort(result)
|
|
171
181
|
return result
|
|
172
182
|
end
|
|
173
183
|
|
|
174
|
-
table.freeze
|
|
184
|
+
table.freeze(NamespaceImpl)
|
|
175
185
|
|
|
176
|
-
-- Public
|
|
186
|
+
-- Public --------------------------------------------------------------
|
|
177
187
|
|
|
178
|
-
local
|
|
188
|
+
local NamespaceModule = {}
|
|
179
189
|
|
|
180
190
|
-- Defines a namespace. Names are auto-prefixed to prevent collisions.
|
|
181
|
-
function
|
|
191
|
+
function NamespaceModule.define(name: string, config: NamespaceConfig): Namespace
|
|
182
192
|
if #name == 0 then
|
|
183
|
-
error
|
|
193
|
+
error("[Lync] Namespace name must not be empty")
|
|
184
194
|
end
|
|
185
195
|
if _registered[name] then
|
|
186
|
-
error
|
|
196
|
+
error(`[Lync] Duplicate namespace: "{name}"`)
|
|
187
197
|
end
|
|
188
198
|
if _count >= MAX_NAMESPACES then
|
|
189
|
-
error
|
|
199
|
+
error(`[Lync] Namespace limit reached: {MAX_NAMESPACES}`)
|
|
190
200
|
end
|
|
191
201
|
|
|
192
202
|
_count += 1
|
|
193
203
|
_registered[name] = true
|
|
194
204
|
|
|
195
|
-
local packets = {} :: { [string]:
|
|
196
|
-
local queries = {} :: { [string]:
|
|
197
|
-
local packetsByShort = {} :: { [string]:
|
|
198
|
-
local queriesByShort = {} :: { [string]:
|
|
199
|
-
local fields = {} :: { [string]: any }
|
|
200
|
-
|
|
201
|
-
fields._name = name
|
|
202
|
-
fields._packets = packets
|
|
203
|
-
fields._queries = queries
|
|
204
|
-
fields._tracked = {} :: { Connection }
|
|
205
|
-
fields._removers = {} :: { () -> () }
|
|
205
|
+
local packets = {} :: { [string]: Packet.Packet }
|
|
206
|
+
local queries = {} :: { [string]: Query.Query }
|
|
207
|
+
local packetsByShort = {} :: { [string]: Packet.Packet }
|
|
208
|
+
local queriesByShort = {} :: { [string]: Query.Query }
|
|
206
209
|
|
|
207
210
|
if config.packets then
|
|
208
211
|
for shortName, packetConfig in config.packets do
|
|
209
|
-
local fullName = prefixName
|
|
210
|
-
local packet = Packet.define
|
|
212
|
+
local fullName = prefixName(name, shortName)
|
|
213
|
+
local packet = Packet.define(fullName, packetConfig)
|
|
211
214
|
packets[fullName] = packet
|
|
212
215
|
packetsByShort[shortName] = packet
|
|
213
|
-
fields[shortName] = packet
|
|
214
216
|
end
|
|
215
217
|
end
|
|
216
218
|
|
|
217
219
|
if config.queries then
|
|
218
220
|
for shortName, queryConfig in config.queries do
|
|
219
|
-
local fullName = prefixName
|
|
220
|
-
local query = Query.define
|
|
221
|
+
local fullName = prefixName(name, shortName)
|
|
222
|
+
local query = Query.define(fullName, queryConfig)
|
|
221
223
|
queries[fullName] = query
|
|
222
224
|
queriesByShort[shortName] = query
|
|
223
|
-
fields[shortName] = query
|
|
224
225
|
end
|
|
225
226
|
end
|
|
226
227
|
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
local nsFields: NamespaceFields = {
|
|
229
|
+
_name = name,
|
|
230
|
+
_packets = packets,
|
|
231
|
+
_queries = queries,
|
|
232
|
+
_tracked = {} :: { Connection },
|
|
233
|
+
_removers = {} :: { () -> () },
|
|
234
|
+
packets = table.freeze(packetsByShort),
|
|
235
|
+
queries = table.freeze(queriesByShort),
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
-- Inject dynamic short-name accessors (ns.Hit, ns.Stats, etc.)
|
|
239
|
+
-- These bypass the typed fields; consumers should prefer ns.packets.X / ns.queries.X.
|
|
240
|
+
local raw = nsFields :: any
|
|
241
|
+
for shortName, packet in packetsByShort do
|
|
242
|
+
raw[shortName] = packet
|
|
243
|
+
end
|
|
244
|
+
for shortName, query in queriesByShort do
|
|
245
|
+
raw[shortName] = query
|
|
246
|
+
end
|
|
229
247
|
|
|
230
|
-
return setmetatable
|
|
248
|
+
return setmetatable(nsFields, NamespaceImpl)
|
|
231
249
|
end
|
|
232
250
|
|
|
233
|
-
return table.freeze
|
|
251
|
+
return table.freeze(NamespaceModule)
|