@axpecter/lync 2.3.2 → 3.0.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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -319
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -188
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -456
  82. package/src/codec/composite/Map.luau +0 -469
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -291
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -147
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -390
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -65
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -211
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
package/Types.luau ADDED
@@ -0,0 +1,215 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The public type vocabulary: every type a consumer names, and the inference behind it.
4
+
5
+ -- Types ---------------------------------------------------------------
6
+
7
+ export type LogKind = "warn" | "error" | "debug"
8
+ export type Cause = "removed" | "cleared"
9
+ export type OutcomeCode = "timeout" | "unanswered" | "leave" | "shutdown"
10
+
11
+ --[[
12
+ The three sentinels. Each carries a phantom field so the checker keeps them apart from one
13
+ another and from any table a caller might pass instead.
14
+ ]]
15
+ export type All = { read _all: true }
16
+ export type Except = { read _except: true }
17
+ export type None = { read _none: true }
18
+
19
+ export type Recipient = All | Except | Group | Player | { Player }
20
+
21
+ --[[
22
+ What `define` takes, keyed by the names the definitions are reached under. Named rather than
23
+ written inline so a caller assembling a namespace has something to annotate.
24
+ ]]
25
+ export type Definition = Packet<any> | Query<any, any> | Set<any>
26
+ export type Definitions = { [string]: Definition }
27
+
28
+ export type Connection = {
29
+ read connected: boolean,
30
+ read disconnect: (self: Connection) -> (),
31
+ }
32
+
33
+ --[[
34
+ Reused between decodes, so anything kept from it must be copied. `last` is stamped on every
35
+ arrival, accepted or not.
36
+ ]]
37
+ export type ValidateContext = {
38
+ read player: Player?,
39
+ read now: number,
40
+ read last: number?,
41
+ }
42
+
43
+ export type LogData = {
44
+ read file: string,
45
+ read line: number,
46
+ read player: Player?,
47
+ read definition: string?,
48
+ read [string]: unknown,
49
+ }
50
+
51
+ export type OutcomeData = {
52
+ read definition: string,
53
+ read elapsed: number?,
54
+ }
55
+
56
+ export type DefinitionStats = {
57
+ read sent: number,
58
+ read sentBytes: number,
59
+ read received: number,
60
+ read receivedBytes: number,
61
+ read drops: number,
62
+ }
63
+
64
+ export type Stats = {
65
+ read flushes: number,
66
+ read sentBytes: number,
67
+ read receivedBytes: number,
68
+ read drops: number,
69
+ read definitions: { [string]: DefinitionStats },
70
+ }
71
+
72
+ --[[
73
+ The phantom `_T` is the whole inference story: it never exists at run time and Infer reads it
74
+ back out below. `monotonic` takes any codec because a generic type may not name itself with a
75
+ different argument. No trend marker sits beside it, an order being derived rather than
76
+ declared.
77
+ ]]
78
+ export type Codec<T> = {
79
+ read _T: T,
80
+ read validate: (
81
+ self: Codec<T>,
82
+ check: (value: T, ctx: ValidateContext) -> string?
83
+ ) -> Codec<T>,
84
+ read as: <U>(self: Codec<T>, to: (value: T) -> U, from: (value: U) -> T) -> Codec<U>,
85
+ read monotonic: (self: Codec<T>) -> Codec<T>,
86
+ read newest: (self: Codec<T>, hz: number?) -> Codec<T>,
87
+ }
88
+
89
+ --[[
90
+ The value a codec carries. `Infer<Codec<Vector3>>` is `Vector3`, which is what lets a handler
91
+ signature fall out of the schema with nothing annotated.
92
+ ]]
93
+ export type function Infer (codec) return codec:readproperty (types.singleton ("_T")) end
94
+
95
+ --[[
96
+ A table of codecs read as the record it describes, mapping each field to the value its codec
97
+ carries. This is what `struct` returns the codec of.
98
+ ]]
99
+ export type function Schema (fields)
100
+ local record = types.newtable ()
101
+ for key, property in fields:properties () do
102
+ local codec = property.read or property.write
103
+ record:setproperty (key, codec:readproperty (types.singleton ("_T")))
104
+ end
105
+ return record
106
+ end
107
+
108
+ --[[
109
+ Every field is optional so a caller may omit it, and the clear sentinel is accepted on exactly
110
+ the fields the record declares optional.
111
+ ]]
112
+ export type function Update (record, none)
113
+ if not (record:is ("table") or record:is ("class")) then return types.any end
114
+
115
+ local fields = types.newtable ()
116
+ for key, property in record:properties () do
117
+ local field = property.read or property.write
118
+ local isClearable = false
119
+ if field:is ("union") then
120
+ for _, part in field:components () do
121
+ if part:is ("nil") then isClearable = true end
122
+ end
123
+ end
124
+ local value = if isClearable then types.unionof (field, none) else field
125
+ fields:setproperty (key, types.optional (value))
126
+ end
127
+ return fields
128
+ end
129
+
130
+ export type Packet<T> = {
131
+ read unreliable: (self: Packet<T>) -> Packet<T>,
132
+ read newest: (self: Packet<T>, hz: number?) -> Packet<T>,
133
+ read timestamped: (self: Packet<T>) -> Packet<T>,
134
+ read fireServer: (self: Packet<T>, value: T) -> (),
135
+ read fireClient: (self: Packet<T>, recipient: Recipient, value: T) -> (),
136
+ read onServer: (
137
+ self: Packet<T>,
138
+ fn: (value: T, player: Player, sent: number?) -> ()
139
+ ) -> Connection,
140
+ read onClient: (self: Packet<T>, fn: (value: T, sent: number?) -> ()) -> Connection,
141
+ read describe: (self: Packet<T>) -> string,
142
+ }
143
+
144
+ --[[
145
+ The client form yields for the reply. The server form takes a completion callback and never
146
+ yields. Both answer `(true, reply)` or `(false, code, data)`, so a request never throws and
147
+ never hangs.
148
+ ]]
149
+ export type Outcome<R> = (ok: boolean, result: R | OutcomeCode, data: OutcomeData?) -> ()
150
+
151
+ export type Query<Q, R> = {
152
+ read onServer: (self: Query<Q, R>, fn: (value: Q, player: Player) -> R) -> (),
153
+ read onClient: (self: Query<Q, R>, fn: (value: Q) -> R) -> (),
154
+ read request: ((
155
+ self: Query<Q, R>,
156
+ value: Q,
157
+ timeout: number?
158
+ ) -> (boolean, R | OutcomeCode, OutcomeData?)) & ((
159
+ self: Query<Q, R>,
160
+ client: Player,
161
+ value: Q,
162
+ complete: Outcome<R>,
163
+ timeout: number?
164
+ ) -> ()),
165
+ read describe: (self: Query<Q, R>) -> string,
166
+ }
167
+
168
+ type SetFields<T> = {
169
+ read keyBy: (self: Set<T>, field: string) -> Set<T>,
170
+ read onAdded: (self: Set<T>, fn: (id: number, record: T) -> ()) -> Connection,
171
+ read onChanged: (self: Set<T>, fn: (id: number, record: T, old: T?) -> ()) -> Connection,
172
+ read onRemoved: (self: Set<T>, fn: (id: number, cause: Cause) -> ()) -> Connection,
173
+ read add: (self: Set<T>, id: number, record: T) -> (),
174
+ read update: (self: Set<T>, id: number, fields: Update<T, None>) -> (),
175
+ read remove: (self: Set<T>, id: number) -> (),
176
+ read clear: (self: Set<T>) -> (),
177
+ read audience: (self: Set<T>, key: unknown, recipient: Recipient) -> (),
178
+ read get: (self: Set<T>, id: number) -> T?,
179
+ read describe: (self: Set<T>) -> string,
180
+ }
181
+
182
+ --[[
183
+ `#set` counts the local view and `for id, record in set do` walks it, so both reads sit on the
184
+ metatable rather than the record.
185
+ ]]
186
+ type SetMeta<T> = {
187
+ __len: (self: Set<T>) -> number,
188
+ __iter: (
189
+ self: Set<T>
190
+ ) -> (({ [number]: T }, number) -> (number?, T), { [number]: T }, number),
191
+ }
192
+
193
+ --[[
194
+ No key parameter. Luau widens a string argument to `string` rather than its singleton, a
195
+ declaration inside the define literal has no annotation site, and a generic type may not name
196
+ itself with a different argument, so a key type could never be carried here. A key on an unkeyed
197
+ set is a programmer error the runtime throws on.
198
+ ]]
199
+ export type Set<T> = typeof(setmetatable ({} :: SetFields<T>, {} :: SetMeta<T>))
200
+
201
+ type GroupFields = {
202
+ read add: (self: Group, player: Player) -> (),
203
+ read remove: (self: Group, player: Player) -> (),
204
+ read has: (self: Group, player: Player) -> boolean,
205
+ read destroy: (self: Group) -> (),
206
+ }
207
+
208
+ type GroupMeta = {
209
+ __len: (self: Group) -> number,
210
+ __iter: (self: Group) -> (({ Player }, number) -> (number?, Player), { Player }, number),
211
+ }
212
+
213
+ export type Group = typeof(setmetatable ({} :: GroupFields, {} :: GroupMeta))
214
+
215
+ return nil
@@ -0,0 +1,154 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- A namespace declared: the names under it, and where a handle finds what it was bound to.
4
+
5
+ local Handles = require (script.Parent.Parent.runtime.Handles)
6
+ local Legality = require (script.Parent.Parent.shape.Legality)
7
+ local Log = require (script.Parent.Parent.kernel.Log)
8
+ local Order = require (script.Parent.Parent.shape.Order)
9
+ local Session = require (script.Parent.Parent.runtime.Session)
10
+ local Types = require (script.Parent.Parent.Types)
11
+
12
+ local Define = {}
13
+
14
+ -- Types ---------------------------------------------------------------
15
+
16
+ --[[
17
+ The binding a handle carries, named here because this is where a caller's half of it is
18
+ written. The other half is filled in by a session, which is why the record is not declared
19
+ here.
20
+ ]]
21
+ export type Live = Handles.Live
22
+
23
+ -- One namespace as declared, before anything has been compiled from it.
24
+ export type Space = {
25
+ read name: string,
26
+ read defs: Types.Definitions,
27
+ read names: { string },
28
+ }
29
+
30
+ -- State ---------------------------------------------------------------
31
+
32
+ -- Every namespace this machine has declared, by the name it was declared under.
33
+ local spaces: { [string]: Space } = {}
34
+ local order: { string } = {}
35
+ local sealed = false
36
+
37
+ -- Private -------------------------------------------------------------
38
+
39
+ local function refuse (said: string, name: string): never
40
+ return Log.error (said, { definition = name })
41
+ end
42
+
43
+ -- What a message calls a definition, which is both halves because either alone names several.
44
+ local function called (of: Live): string return `{of.space}.{of.name}` end
45
+
46
+ -- Public --------------------------------------------------------------
47
+
48
+ Define.spaces = spaces
49
+ Define.order = order
50
+ Define.refuse = refuse
51
+
52
+ -- What this definition is called, which is both halves where it has them.
53
+ function Define.naming (of: Live, said: string): string
54
+ if of.space == nil then return said end
55
+ return called (of)
56
+ end
57
+
58
+ -- A modifier, refused once the namespace it is in has been started.
59
+ function Define.declaring (of: Live, said: string)
60
+ if of.session == nil then return end
61
+
62
+ refuse (
63
+ `{said} was called after {of.space} started, and a declaration is not a setting`,
64
+ called (of)
65
+ )
66
+ end
67
+
68
+ -- The session a handle talks through, or nothing where it has none yet.
69
+ function Define.reach (of: Live, said: string): Session.Session
70
+ local session = of.session
71
+ if session == nil then
72
+ if of.space == nil then
73
+ refuse (`{said} before it was named in a namespace, which define is for`, said)
74
+ end
75
+ refuse (`{said} before {of.space} was started`, called (of))
76
+ end
77
+
78
+ local held = session :: Session.Session
79
+ if held.closed then
80
+ refuse (`{said} after {of.space} was closed, and close lets its remotes go`, called (of))
81
+ end
82
+
83
+ return held
84
+ end
85
+
86
+ -- A call only the server makes, since the records and the audiences are the server's.
87
+ function Define.serving (of: Live, said: string): Session.Session
88
+ local session = Define.reach (of, said)
89
+ if not of.isServer then
90
+ refuse (`{said} is the server's call, and this is a client`, called (of))
91
+ end
92
+
93
+ return session
94
+ end
95
+
96
+ -- A call only a client makes, since what it leaves out is the one machine a client can reach.
97
+ function Define.asking (of: Live, said: string): Session.Session
98
+ local session = Define.reach (of, said)
99
+ if of.isServer then
100
+ refuse (`{said} is a client's call, and this is the server`, called (of))
101
+ end
102
+
103
+ return session
104
+ end
105
+
106
+ -- No more namespaces, which is what start says about the ones it did not find.
107
+ function Define.seal () sealed = true end
108
+
109
+ --[[
110
+ A namespace, as the definitions a caller wrote and the name both machines require it under. The
111
+ table handed over is the one handed back, frozen, so a caller who edited it afterwards cannot
112
+ leave the two machines agreeing at different times. A name already defined refuses.
113
+ ]]
114
+ function Define.define (name: string, defs: Types.Definitions): Types.Definitions
115
+ if sealed then
116
+ refuse (`{name} was defined after start, and start opens what there is`, name)
117
+ end
118
+ if spaces[name] ~= nil then refuse (`{name} has already been defined`, name) end
119
+
120
+ local why = Legality.named (name)
121
+ if why ~= nil then refuse (`{name} {why}`, name) end
122
+ if name == "" then refuse ("a namespace with no name cannot be required by anybody", name) end
123
+
124
+ local names = Order.names (defs)
125
+ for _, under in names do
126
+ --[[
127
+ Read through a check that the value is a table at all. A caller who wrote a number
128
+ against a name has made the typo this refusal is for, and reaching into it first turns
129
+ that into an index of a number, which says nothing about what they got wrong.
130
+ ]]
131
+ local one = defs[under] :: any
132
+ local live = if typeof (one) == "table" then one.live :: Live? else nil
133
+ if live == nil then refuse (`{name}.{under} is not a packet, a query or a set`, name) end
134
+
135
+ local held = live :: Live
136
+ if held.space ~= nil then
137
+ refuse (`{name}.{under} is already named in {held.space}`, name)
138
+ end
139
+
140
+ held.space = name
141
+ held.name = under
142
+ end
143
+
144
+ spaces[name] = table.freeze ({ name = name, defs = defs, names = table.freeze (names) })
145
+ table.insert (order, name)
146
+
147
+ --[[
148
+ Frozen where it is not already. A caller who froze their own table handed over one that is
149
+ as unwritable as this asks for, and freezing a frozen table raises rather than agreeing.
150
+ ]]
151
+ return if table.isfrozen (defs) then defs else table.freeze (defs)
152
+ end
153
+
154
+ return table.freeze (Define)
@@ -0,0 +1,119 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The place's lifecycle: the one start, the flushes that send, and the close that lets go.
4
+
5
+ local Define = require (script.Parent.Define)
6
+ local Log = require (script.Parent.Parent.kernel.Log)
7
+ local Session = require (script.Parent.Parent.runtime.Session)
8
+ local Transport = require (script.Parent.Parent.channel.Transport)
9
+ local Types = require (script.Parent.Parent.Types)
10
+
11
+ local Lifecycle = {}
12
+
13
+ -- State ---------------------------------------------------------------
14
+
15
+ --[[
16
+ Every namespace that has been opened, under the name it was declared with.
17
+
18
+ Module state for the reason the namespace table is: a place starts once, whatever number of
19
+ scripts declared into it, and every call here is about all of them or about one by name.
20
+ ]]
21
+ local sessions: { [string]: Session.Session } = {}
22
+ local started = false
23
+
24
+ -- Private -------------------------------------------------------------
25
+
26
+ -- Who is here, as the three things a namespace asks and no more.
27
+ local function rostered (): Session.Roster
28
+ local players = game:GetService ("Players")
29
+
30
+ return {
31
+ all = function (): { Player } return players:GetPlayers () end,
32
+ onAdded = function (_, take: (Player) -> ()): () -> ()
33
+ local held = players.PlayerAdded:Connect (take)
34
+ return function () held:Disconnect () end
35
+ end,
36
+ -- Wrapped, unlike the one above it, because this event hands over a reason as well.
37
+ onRemoving = function (_, take: (Player) -> ()): () -> ()
38
+ local held = players.PlayerRemoving:Connect (function (who: Player) take (who) end)
39
+ return function () held:Disconnect () end
40
+ end,
41
+ }
42
+ end
43
+
44
+ -- The namespace a name reaches, refused where it reaches none.
45
+ local function opened (name: string, said: string): Session.Session
46
+ local session = sessions[name]
47
+ if session == nil then
48
+ Log.error (`{said} named {name}, which is no namespace this place defined`, {
49
+ definition = name,
50
+ })
51
+ end
52
+
53
+ return session :: Session.Session
54
+ end
55
+
56
+ -- Nothing here is about a definition, so a refusal about the place itself names none.
57
+ local function running (said: string)
58
+ if started then return end
59
+
60
+ Log.error (`{said} before start, and nothing is open until a place starts`)
61
+ end
62
+
63
+ -- Public --------------------------------------------------------------
64
+
65
+ -- Every namespace declared, compiled, bound to its handles, and opened.
66
+ function Lifecycle.start ()
67
+ if started then Log.error ("start has already run, and a place starts once") end
68
+ started = true
69
+ Define.seal ()
70
+
71
+ local side: Transport.Side = if game:GetService ("RunService"):IsServer ()
72
+ then "server"
73
+ else "client"
74
+ local roster = rostered ()
75
+
76
+ for _, name in Define.order do
77
+ local space = Define.spaces[name]
78
+ local transport = Transport.new (name, side)
79
+ local session = Session.new (name, space.defs, transport, roster, side)
80
+
81
+ sessions[name] = session
82
+ session:start ()
83
+ end
84
+ end
85
+
86
+ -- Everything buffered since the last flush, out, for every namespace or for one by name.
87
+ function Lifecycle.flush (first: (string | number)?, second: number?)
88
+ running ("flush")
89
+
90
+ if type (first) == "string" then
91
+ opened (first, "flush"):flush (second)
92
+ return
93
+ end
94
+
95
+ for _, name in Define.order do
96
+ sessions[name]:flush (first)
97
+ end
98
+ end
99
+
100
+ --[[
101
+ Every namespace closed, in the order they were declared. A second call is not a fault: what
102
+ close promises is that the remotes are let go and nothing is left waiting, and both are true
103
+ the second time.
104
+ ]]
105
+ function Lifecycle.close ()
106
+ running ("close")
107
+
108
+ for _, name in Define.order do
109
+ sessions[name]:close ()
110
+ end
111
+ end
112
+
113
+ -- One namespace's counters, as the frozen reading two of which subtract into a rate.
114
+ function Lifecycle.stats (name: string): Types.Stats
115
+ running ("stats")
116
+ return opened (name, "stats"):stats ()
117
+ end
118
+
119
+ return table.freeze (Lifecycle)
@@ -0,0 +1,108 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The packet handle: what a caller declares before start, and what they fire and hear after it.
4
+
5
+ local Base = require (script.Parent.Parent.codec.Base)
6
+ local Define = require (script.Parent.Define)
7
+ local Handles = require (script.Parent.Parent.runtime.Handles)
8
+ local Legality = require (script.Parent.Parent.shape.Legality)
9
+ local Serial = require (script.Parent.Parent.shape.Serial)
10
+ local Types = require (script.Parent.Parent.Types)
11
+
12
+ local Packet = {}
13
+ Packet.__index = Packet
14
+
15
+ -- Private -------------------------------------------------------------
16
+
17
+ local function hears (self: any, fn: (...any) -> ()): Types.Connection
18
+ local live = self.live
19
+
20
+ -- The id is read inside, because a handle attached before it was bound has none yet.
21
+ return Handles.later (live, function (of) return of:listen (live.id :: number, fn) end)
22
+ end
23
+
24
+ -- Public --------------------------------------------------------------
25
+
26
+ function Packet.new <T>(of: Types.Codec<T>): Types.Packet<T>
27
+ --[[
28
+ Level two is the caller, so a schema refused at start names the line a caller declared on
29
+ rather than the line that found the fault.
30
+ ]]
31
+ local file, line = debug.info (2, "sl")
32
+ local self = {
33
+ kind = "packet" :: "packet",
34
+ of = of,
35
+ isUnreliable = false,
36
+ isNewest = false,
37
+ hz = nil :: number?,
38
+ isTimestamped = false,
39
+ at = { file = file, line = line },
40
+ live = Handles.live (),
41
+ }
42
+
43
+ return (setmetatable (self, Packet) :: any) :: Types.Packet<T>
44
+ end
45
+
46
+ -- Lossy and unordered, and every fire still goes out.
47
+ function Packet.unreliable (self: any): any
48
+ Define.declaring (self.live, "unreliable")
49
+ self.isUnreliable = true
50
+ return self
51
+ end
52
+
53
+ -- Lossy, latest wins, and rate capped where a rate is named.
54
+ function Packet.newest (self: any, hz: number?): any
55
+ Define.declaring (self.live, "newest")
56
+ self.isNewest = true
57
+ self.hz = hz
58
+ return self
59
+ end
60
+
61
+ -- Stamped with the instant it was fired, which is comparable across clients.
62
+ function Packet.timestamped (self: any): any
63
+ Define.declaring (self.live, "timestamped")
64
+ self.isTimestamped = true
65
+ return self
66
+ end
67
+
68
+ --[[
69
+ One fire at the server, which is the only place a client can reach.
70
+
71
+ No recipient, because there is one. That asymmetry is the surface's own and is not composed
72
+ here: a client naming who to send to would be naming the machine it is already talking to.
73
+ ]]
74
+ function Packet.fireServer (self: any, value: any)
75
+ local session = Define.asking (self.live, "fireServer")
76
+ session:fire (self.live.def, nil, value)
77
+ end
78
+
79
+ -- One fire at whoever the recipient names, which only a server has anybody to name.
80
+ function Packet.fireClient (self: any, to: Types.Recipient, value: any)
81
+ local session = Define.serving (self.live, "fireClient")
82
+ session:fire (self.live.def, to, value)
83
+ end
84
+
85
+ --[[
86
+ The number both listeners take last is the instant of the fire, and it arrives only where the
87
+ packet was declared stamped. Every other fire hands the listener nothing in its place.
88
+ ]]
89
+ function Packet.onServer (self: any, fn: (any, Player, number?) -> ()): Types.Connection
90
+ return hears (self, fn)
91
+ end
92
+
93
+ function Packet.onClient (self: any, fn: (any, number?) -> ()): Types.Connection
94
+ return hears (self, fn)
95
+ end
96
+
97
+ -- The schema as text, which is what a report quotes back when a decode is refused.
98
+ function Packet.describe (self: any): string
99
+ return Serial.frame ({
100
+ kind = "packet",
101
+ of = { cell = Base.cell (self.of) },
102
+ delivery = Legality.delivery (self),
103
+ hz = self.hz,
104
+ timestamped = self.isTimestamped,
105
+ })
106
+ end
107
+
108
+ return table.freeze (Packet)