@axpecter/lync 2.3.3 → 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.
- package/LICENSE +21 -0
- package/README.md +302 -299
- package/Types.luau +215 -0
- package/api/Define.luau +154 -0
- package/api/Lifecycle.luau +119 -0
- package/api/Packet.luau +108 -0
- package/api/Query.luau +149 -0
- package/api/Set.luau +189 -0
- package/channel/Blocks.luau +124 -0
- package/channel/Group.luau +118 -0
- package/channel/Link.luau +135 -0
- package/channel/Outbox.luau +261 -0
- package/channel/Recipients.luau +129 -0
- package/channel/Transport.luau +138 -0
- package/codec/Base.luau +236 -0
- package/codec/Composites.luau +128 -0
- package/codec/Datatypes.luau +162 -0
- package/codec/Legality.luau +61 -0
- package/codec/Scalars.luau +72 -0
- package/codec/Text.luau +89 -0
- package/codec/init.luau +39 -0
- package/index.d.ts +285 -0
- package/init.luau +91 -0
- package/kernel/Bits.luau +16 -0
- package/kernel/Catalog.luau +121 -0
- package/kernel/Heap.luau +80 -0
- package/kernel/Listeners.luau +95 -0
- package/kernel/Log.luau +197 -0
- package/kernel/Tally.luau +128 -0
- package/kernel/Threads.luau +44 -0
- package/kernel/Wide.luau +267 -0
- package/package.json +15 -32
- package/runtime/Boundary.luau +180 -0
- package/runtime/Dispatch.luau +91 -0
- package/runtime/Handles.luau +114 -0
- package/runtime/Handshake.luau +90 -0
- package/runtime/Pending.luau +196 -0
- package/runtime/Router.luau +379 -0
- package/runtime/Session.luau +731 -0
- package/shape/Compile.luau +340 -0
- package/shape/Folds.luau +332 -0
- package/shape/Frames.luau +170 -0
- package/shape/Grid.luau +169 -0
- package/shape/Ir.luau +540 -0
- package/shape/Joins.luau +125 -0
- package/shape/Legality.luau +273 -0
- package/shape/Lower.luau +419 -0
- package/shape/Order.luau +64 -0
- package/shape/Regions.luau +113 -0
- package/shape/Rotation.luau +472 -0
- package/shape/Serial.luau +258 -0
- package/shape/Words.luau +89 -0
- package/shape/emit/Block.luau +453 -0
- package/shape/emit/Column.luau +338 -0
- package/shape/emit/Leaf.luau +462 -0
- package/shape/emit/Word.luau +234 -0
- package/shape/emit/init.luau +222 -0
- package/shape/init.luau +78 -0
- package/state/Apply.luau +330 -0
- package/state/Audience.luau +126 -0
- package/state/Catchup.luau +218 -0
- package/state/Coalesce.luau +43 -0
- package/state/Fieldset.luau +177 -0
- package/state/Newest.luau +339 -0
- package/state/Plan.luau +441 -0
- package/state/Sections.luau +468 -0
- package/state/Store.luau +188 -0
- package/state/Sync.luau +762 -0
- package/wire/Bytes.luau +118 -0
- package/wire/Clock.luau +37 -0
- package/wire/Protocol.luau +110 -0
- package/wire/Sequence.luau +40 -0
- package/wire/Varint.luau +124 -0
- package/src/Types.luau +0 -180
- package/src/api/Group.luau +0 -156
- package/src/api/Packet.luau +0 -317
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -193
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -473
- package/src/codec/composite/Map.luau +0 -485
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -311
- package/src/codec/composite/Struct.luau +0 -494
- package/src/codec/composite/Tagged.luau +0 -103
- package/src/codec/composite/Tuple.luau +0 -143
- package/src/codec/datatype/Buffer.luau +0 -66
- package/src/codec/datatype/CFrame.luau +0 -109
- package/src/codec/datatype/Color.luau +0 -32
- package/src/codec/datatype/Instance.luau +0 -52
- package/src/codec/datatype/IntVector.luau +0 -39
- package/src/codec/datatype/NumberRange.luau +0 -39
- package/src/codec/datatype/Ray.luau +0 -36
- package/src/codec/datatype/Rect.luau +0 -31
- package/src/codec/datatype/Region.luau +0 -62
- package/src/codec/datatype/Sequence.luau +0 -184
- package/src/codec/datatype/String.luau +0 -94
- package/src/codec/datatype/UDim.luau +0 -46
- package/src/codec/datatype/Vector.luau +0 -130
- package/src/codec/meta/Auto.luau +0 -318
- package/src/codec/meta/Bitfield.luau +0 -160
- package/src/codec/meta/Custom.luau +0 -33
- package/src/codec/meta/DeltaScalar.luau +0 -390
- package/src/codec/meta/Enum.luau +0 -50
- package/src/codec/meta/Float.luau +0 -38
- package/src/codec/meta/Nothing.luau +0 -13
- package/src/codec/meta/Unknown.luau +0 -41
- package/src/codec/primitive/Bool.luau +0 -26
- package/src/codec/primitive/Float16.luau +0 -99
- package/src/codec/primitive/Int.luau +0 -59
- package/src/codec/primitive/Number.luau +0 -24
- package/src/codec/primitive/Signed.luau +0 -32
- package/src/codec/primitive/Varint.luau +0 -165
- package/src/codec/primitive/Zint.luau +0 -99
- package/src/index.d.ts +0 -423
- package/src/init.luau +0 -362
- package/src/internal/Baseline.luau +0 -57
- package/src/internal/Channel.luau +0 -484
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -66
- package/src/internal/Registry.luau +0 -119
- package/src/internal/Transport.luau +0 -28
- package/src/transport/Bridge.luau +0 -110
- package/src/transport/Client.luau +0 -97
- package/src/transport/Gate.luau +0 -488
- package/src/transport/Reader.luau +0 -331
- package/src/transport/Server.luau +0 -233
- package/src/util/Array.luau +0 -18
- package/src/util/Buffer.luau +0 -90
- package/src/util/Constants.luau +0 -30
- package/src/util/Log.luau +0 -68
- package/src/util/Player.luau +0 -14
- package/src/util/Quantize.luau +0 -84
- 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
|
package/api/Define.luau
ADDED
|
@@ -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)
|
package/api/Packet.luau
ADDED
|
@@ -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)
|