@trioforge/crucible 0.1.0 → 0.3.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/maths/spline/package.json +4 -0
- package/out/client/index.d.ts +7 -0
- package/out/client/init.luau +36 -1
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/data.d.ts +224 -0
- package/out/data.luau +97 -0
- package/out/emit/configs.d.ts +4 -0
- package/out/emit/configs.luau +22 -0
- package/out/emit/data.d.ts +6 -0
- package/out/emit/data.luau +19 -0
- package/out/emit/index.d.ts +5 -1
- package/out/emit/init.luau +23 -7
- package/out/emit/network.d.ts +6 -0
- package/out/emit/network.luau +36 -0
- package/out/emit/replicated.d.ts +4 -0
- package/out/emit/replicated.luau +13 -0
- package/out/index.d.ts +8 -0
- package/out/init.luau +23 -0
- package/out/internal/binding.luau +5 -0
- package/out/internal/boot.luau +112 -8
- package/out/internal/configs/limits.d.ts +10 -0
- package/out/internal/configs/limits.luau +25 -0
- package/out/internal/configs/port.d.ts +29 -0
- package/out/internal/configs/port.luau +99 -0
- package/out/internal/configs/runtime.d.ts +21 -0
- package/out/internal/configs/runtime.luau +981 -0
- package/out/internal/data/admission.d.ts +1 -0
- package/out/internal/data/admission.luau +673 -0
- package/out/internal/data/declarations.d.ts +1 -0
- package/out/internal/data/declarations.luau +384 -0
- package/out/internal/data/envelope.d.ts +1 -0
- package/out/internal/data/envelope.luau +318 -0
- package/out/internal/data/handles.d.ts +1 -0
- package/out/internal/data/handles.luau +255 -0
- package/out/internal/data/inbox-transforms.d.ts +1 -0
- package/out/internal/data/inbox-transforms.luau +458 -0
- package/out/internal/data/inbox.d.ts +1 -0
- package/out/internal/data/inbox.luau +437 -0
- package/out/internal/data/lease.d.ts +1 -0
- package/out/internal/data/lease.luau +304 -0
- package/out/internal/data/limits.d.ts +1 -0
- package/out/internal/data/limits.luau +234 -0
- package/out/internal/data/messages.d.ts +1 -0
- package/out/internal/data/messages.luau +69 -0
- package/out/internal/data/operation-id.d.ts +1 -0
- package/out/internal/data/operation-id.luau +61 -0
- package/out/internal/data/paths.d.ts +2 -0
- package/out/internal/data/paths.luau +323 -0
- package/out/internal/data/port.d.ts +1 -0
- package/out/internal/data/port.luau +290 -0
- package/out/internal/data/profiles.d.ts +1 -0
- package/out/internal/data/profiles.luau +862 -0
- package/out/internal/data/receipts.d.ts +1 -0
- package/out/internal/data/receipts.luau +190 -0
- package/out/internal/data/record-bindings.d.ts +1 -0
- package/out/internal/data/record-bindings.luau +92 -0
- package/out/internal/data/record-declarations.d.ts +1 -0
- package/out/internal/data/record-declarations.luau +140 -0
- package/out/internal/data/records.d.ts +1 -0
- package/out/internal/data/records.luau +400 -0
- package/out/internal/data/runtime.d.ts +1 -0
- package/out/internal/data/runtime.luau +918 -0
- package/out/internal/data/scheduler.d.ts +1 -0
- package/out/internal/data/scheduler.luau +507 -0
- package/out/internal/data/session.d.ts +1 -0
- package/out/internal/data/session.luau +234 -0
- package/out/internal/data/shutdown.d.ts +1 -0
- package/out/internal/data/shutdown.luau +91 -0
- package/out/internal/data/waiters.d.ts +1 -0
- package/out/internal/data/waiters.luau +65 -0
- package/out/internal/lifecycle.luau +45 -0
- package/out/internal/network/catalog.d.ts +26 -0
- package/out/internal/network/catalog.luau +182 -0
- package/out/internal/network/claims.d.ts +1 -0
- package/out/internal/network/claims.luau +146 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +405 -0
- package/out/internal/network/codec.d.ts +63 -0
- package/out/internal/network/codec.luau +1183 -0
- package/out/internal/network/envelope.d.ts +1 -0
- package/out/internal/network/envelope.luau +247 -0
- package/out/internal/network/limits.d.ts +26 -0
- package/out/internal/network/limits.luau +55 -0
- package/out/internal/network/port.d.ts +1 -0
- package/out/internal/network/port.luau +19 -0
- package/out/internal/network/roblox-network-port.d.ts +1 -0
- package/out/internal/network/roblox-network-port.luau +105 -0
- package/out/internal/network/schema.d.ts +72 -0
- package/out/internal/network/schema.luau +386 -0
- package/out/internal/network/server-session.d.ts +1 -0
- package/out/internal/network/server-session.luau +395 -0
- package/out/internal/network/session.d.ts +1 -0
- package/out/internal/network/session.luau +266 -0
- package/out/internal/reactive/context.d.ts +1 -0
- package/out/internal/reactive/context.luau +149 -0
- package/out/internal/reactive/flush.d.ts +1 -0
- package/out/internal/reactive/flush.luau +355 -0
- package/out/internal/reactive/graph.d.ts +1 -0
- package/out/internal/reactive/graph.luau +626 -0
- package/out/internal/reactive/limits.d.ts +1 -0
- package/out/internal/reactive/limits.luau +54 -0
- package/out/internal/reactive/ownership.d.ts +1 -0
- package/out/internal/reactive/ownership.luau +626 -0
- package/out/internal/registry.luau +10 -1
- package/out/internal/sync/client-sync.d.ts +1 -0
- package/out/internal/sync/client-sync.luau +157 -0
- package/out/internal/sync/declarations.d.ts +1 -0
- package/out/internal/sync/declarations.luau +276 -0
- package/out/internal/sync/frame.d.ts +1 -0
- package/out/internal/sync/frame.luau +91 -0
- package/out/internal/sync/server-sync.d.ts +1 -0
- package/out/internal/sync/server-sync.luau +502 -0
- package/out/internal/sync/values.d.ts +1 -0
- package/out/internal/sync/values.luau +930 -0
- package/out/maths/spline/curve.d.ts +35 -0
- package/out/maths/spline/curve.luau +1122 -0
- package/out/maths/spline/index.d.ts +2 -0
- package/out/maths/spline/init.luau +8 -0
- package/out/network.d.ts +66 -0
- package/out/network.luau +24 -0
- package/out/reactive.d.ts +76 -0
- package/out/reactive.luau +136 -0
- package/out/replicated.d.ts +38 -0
- package/out/replicated.luau +45 -0
- package/out/server/data.d.ts +54 -0
- package/out/server/data.luau +44 -0
- package/out/server/index.d.ts +17 -0
- package/out/server/init.luau +58 -4
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/testing/index.d.ts +38 -0
- package/out/testing/init.luau +189 -0
- package/out/util/random/index.d.ts +24 -0
- package/out/util/random/init.luau +301 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +28 -2
- package/testing/package.json +4 -0
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/index.js +87 -23
- package/transformer/network.cjs +961 -0
- package/util/random/package.json +4 -0
- package/vide/package.json +4 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local bootClock = TS.import(script, script.Parent.Parent, "boot").bootClock
|
|
4
|
+
local _graph = TS.import(script, script.Parent.Parent, "reactive", "graph")
|
|
5
|
+
local createStateNode = _graph.createStateNode
|
|
6
|
+
local update = _graph.update
|
|
7
|
+
local call = TS.import(script, script.Parent.Parent, "runner").call
|
|
8
|
+
local admitWrite = TS.import(script, script.Parent, "admission").admitWrite
|
|
9
|
+
local jsonBytes = TS.import(script, script.Parent, "envelope").jsonBytes
|
|
10
|
+
local PAYLOAD_MAX_BYTES = TS.import(script, script.Parent, "limits").PAYLOAD_MAX_BYTES
|
|
11
|
+
--* @internal `confirmed` before any owned write with the session's token is confirmed (rule 37).
|
|
12
|
+
local NONE = -1
|
|
13
|
+
--[[
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
* Loading --acquired, onLoad--> Active --leave / handoff / final phase / authority lost--> Sealed --release--> Ended
|
|
17
|
+
* Loading --failed, left or closed: abandon only--> Ended
|
|
18
|
+
|
|
19
|
+
]]
|
|
20
|
+
--* @internal
|
|
21
|
+
--* @internal How a release settled: its write committed, or it was fenced or gave up (rule 29).
|
|
22
|
+
local callbackThreads = {}
|
|
23
|
+
--* Data never writes this state: its `update` frame is what makes reactive reject a nested write (rule 13, K7).
|
|
24
|
+
local transaction = createStateNode(0)
|
|
25
|
+
local depth = 0
|
|
26
|
+
--[[
|
|
27
|
+
*
|
|
28
|
+
* @internal Runs a data callback on a no-yield worker whose thread may not write data, inside the `update` of a private
|
|
29
|
+
* reactive state so a state, publish or apply write throws (rule 13); a yield or throw is thrown here.
|
|
30
|
+
|
|
31
|
+
]]
|
|
32
|
+
local function dataCallback(work, a, b)
|
|
33
|
+
local worker
|
|
34
|
+
depth += 1
|
|
35
|
+
local status, value = call(function(x, y)
|
|
36
|
+
worker = coroutine.running()
|
|
37
|
+
local _worker = worker
|
|
38
|
+
callbackThreads[_worker] = true
|
|
39
|
+
local result
|
|
40
|
+
update(transaction, function(current)
|
|
41
|
+
result = work(x, y)
|
|
42
|
+
return current
|
|
43
|
+
end)
|
|
44
|
+
return result
|
|
45
|
+
end, a, b)
|
|
46
|
+
depth -= 1
|
|
47
|
+
if worker ~= nil then
|
|
48
|
+
local _worker = worker
|
|
49
|
+
callbackThreads[_worker] = nil
|
|
50
|
+
end
|
|
51
|
+
if status ~= 0 then
|
|
52
|
+
error(value, 0)
|
|
53
|
+
end
|
|
54
|
+
return value
|
|
55
|
+
end
|
|
56
|
+
--[[
|
|
57
|
+
*
|
|
58
|
+
* @internal True while any data callback runs. Callbacks never yield, so code running then was started inside one,
|
|
59
|
+
* on its thread or a thread it spawned (rule 123).
|
|
60
|
+
|
|
61
|
+
]]
|
|
62
|
+
local function insideDataCallback()
|
|
63
|
+
return depth > 0
|
|
64
|
+
end
|
|
65
|
+
--* @internal Rule 13: no data write on a data callback's own thread.
|
|
66
|
+
local function assertOutsideCallback()
|
|
67
|
+
local _arg0 = coroutine.running()
|
|
68
|
+
if callbackThreads[_arg0] ~= nil then
|
|
69
|
+
error("a write inside a data callback; return the data instead", 3)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
--* @internal One player's profile on this server, and the only writer of its data (rules 12 to 16).
|
|
73
|
+
local Session
|
|
74
|
+
do
|
|
75
|
+
Session = setmetatable({}, {
|
|
76
|
+
__tostring = function()
|
|
77
|
+
return "Session"
|
|
78
|
+
end,
|
|
79
|
+
})
|
|
80
|
+
Session.__index = Session
|
|
81
|
+
function Session.new(...)
|
|
82
|
+
local self = setmetatable({}, Session)
|
|
83
|
+
return self:constructor(...) or self
|
|
84
|
+
end
|
|
85
|
+
function Session:constructor(schema, player, userId, token, hooks)
|
|
86
|
+
self.schema = schema
|
|
87
|
+
self.player = player
|
|
88
|
+
self.userId = userId
|
|
89
|
+
self.token = token
|
|
90
|
+
self.hooks = hooks
|
|
91
|
+
self.phase = 0
|
|
92
|
+
self.abandoned = false
|
|
93
|
+
self.epoch = 0
|
|
94
|
+
self.safeUntil = 0
|
|
95
|
+
self.revision = 0
|
|
96
|
+
self.confirmed = NONE
|
|
97
|
+
self.op = 0
|
|
98
|
+
self.busy = false
|
|
99
|
+
self.receipts = {}
|
|
100
|
+
self.durable = {}
|
|
101
|
+
self.effects = {}
|
|
102
|
+
self.acks = {}
|
|
103
|
+
self.marks = {}
|
|
104
|
+
self.inbox = {}
|
|
105
|
+
self.retryAt = {}
|
|
106
|
+
self.carrying = NONE
|
|
107
|
+
self.lost = NONE
|
|
108
|
+
self.releaseWanted = false
|
|
109
|
+
self.releaseAmbiguous = false
|
|
110
|
+
self.renewAt = 0
|
|
111
|
+
end
|
|
112
|
+
function Session:writable()
|
|
113
|
+
return self.phase == 1 and bootClock() < self.safeUntil
|
|
114
|
+
end
|
|
115
|
+
function Session:snapshot()
|
|
116
|
+
local applied = {}
|
|
117
|
+
for id, revision in self.acks do
|
|
118
|
+
local _arg0 = { id, revision }
|
|
119
|
+
table.insert(applied, _arg0)
|
|
120
|
+
end
|
|
121
|
+
table.sort(applied, function(a, b)
|
|
122
|
+
return a[2] < b[2]
|
|
123
|
+
end)
|
|
124
|
+
-- ▼ ReadonlyArray.map ▼
|
|
125
|
+
local _newValue = table.create(#applied)
|
|
126
|
+
local _callback = function(_param)
|
|
127
|
+
local id = _param[1]
|
|
128
|
+
return id
|
|
129
|
+
end
|
|
130
|
+
for _k, _v in applied do
|
|
131
|
+
_newValue[_k] = _callback(_v, _k - 1, applied)
|
|
132
|
+
end
|
|
133
|
+
-- ▲ ReadonlyArray.map ▲
|
|
134
|
+
local acks = _newValue
|
|
135
|
+
local marks = {}
|
|
136
|
+
for _, mark in self.marks do
|
|
137
|
+
table.insert(marks, mark)
|
|
138
|
+
end
|
|
139
|
+
local _object = {
|
|
140
|
+
revision = self.revision,
|
|
141
|
+
schemaVersion = self.schema.version,
|
|
142
|
+
payload = self.value,
|
|
143
|
+
}
|
|
144
|
+
local _left = "receipts"
|
|
145
|
+
local _array = {}
|
|
146
|
+
local _length = #_array
|
|
147
|
+
local _array_1 = self.receipts
|
|
148
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
149
|
+
_object[_left] = _array
|
|
150
|
+
_object.acks = acks
|
|
151
|
+
_object.marks = marks
|
|
152
|
+
return _object
|
|
153
|
+
end
|
|
154
|
+
function Session:uncertain()
|
|
155
|
+
local ticket = self.writing
|
|
156
|
+
local _result = ticket
|
|
157
|
+
if _result ~= nil then
|
|
158
|
+
_result = _result.dispatched
|
|
159
|
+
end
|
|
160
|
+
local _condition = _result == true
|
|
161
|
+
if _condition then
|
|
162
|
+
_condition = (ticket.inFlight or ticket.ambiguous)
|
|
163
|
+
end
|
|
164
|
+
local pending = if _condition then self.carrying else NONE
|
|
165
|
+
return math.max(pending, self.lost)
|
|
166
|
+
end
|
|
167
|
+
function Session:assertIdle()
|
|
168
|
+
if self.busy then
|
|
169
|
+
error(`{self.schema.name}: {self.userId}'s profile is inside a data callback; return the data instead`, 3)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
function Session:transact(work)
|
|
173
|
+
self:assertIdle()
|
|
174
|
+
self.busy = true
|
|
175
|
+
local ok, result = pcall(work)
|
|
176
|
+
self.busy = false
|
|
177
|
+
self.hooks.changed()
|
|
178
|
+
if not ok then
|
|
179
|
+
error(result, 0)
|
|
180
|
+
end
|
|
181
|
+
return result
|
|
182
|
+
end
|
|
183
|
+
function Session:callback(work, a, b)
|
|
184
|
+
return dataCallback(work, a, b)
|
|
185
|
+
end
|
|
186
|
+
function Session:prepare(value)
|
|
187
|
+
local admitted = admitWrite(self.schema, value)
|
|
188
|
+
local bytes = jsonBytes(admitted)
|
|
189
|
+
if bytes > PAYLOAD_MAX_BYTES then
|
|
190
|
+
error(`{self.schema.name}: {self.userId}'s profile is {bytes} bytes, over {PAYLOAD_MAX_BYTES}`, 0)
|
|
191
|
+
end
|
|
192
|
+
local _result = self.schema.check
|
|
193
|
+
if _result ~= nil then
|
|
194
|
+
_result(admitted, self.value)
|
|
195
|
+
end
|
|
196
|
+
return admitted
|
|
197
|
+
end
|
|
198
|
+
function Session:commit(value, stage)
|
|
199
|
+
self.value = value
|
|
200
|
+
local _result = stage
|
|
201
|
+
if _result ~= nil then
|
|
202
|
+
_result()
|
|
203
|
+
end
|
|
204
|
+
self.revision += 1
|
|
205
|
+
end
|
|
206
|
+
function Session:write(change, stage)
|
|
207
|
+
return self:transact(function()
|
|
208
|
+
local current = self.value
|
|
209
|
+
local result = self:callback(change, current, nil)
|
|
210
|
+
if rawequal(result, current) and stage == nil then
|
|
211
|
+
return false
|
|
212
|
+
end
|
|
213
|
+
self:commit(self:prepare(result), stage)
|
|
214
|
+
self.hooks.publish(self.value)
|
|
215
|
+
return true
|
|
216
|
+
end)
|
|
217
|
+
end
|
|
218
|
+
function Session:stage(id)
|
|
219
|
+
local _receipts = self.receipts
|
|
220
|
+
local _id = id
|
|
221
|
+
if not (table.find(_receipts, _id) ~= nil) then
|
|
222
|
+
local _receipts_1 = self.receipts
|
|
223
|
+
local _id_1 = id
|
|
224
|
+
table.insert(_receipts_1, _id_1)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
return {
|
|
229
|
+
dataCallback = dataCallback,
|
|
230
|
+
insideDataCallback = insideDataCallback,
|
|
231
|
+
assertOutsideCallback = assertOutsideCallback,
|
|
232
|
+
NONE = NONE,
|
|
233
|
+
Session = Session,
|
|
234
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _boot = TS.import(script, script.Parent.Parent, "boot")
|
|
4
|
+
local bootClock = _boot.bootClock
|
|
5
|
+
local shutdownProgress = _boot.shutdownProgress
|
|
6
|
+
local _limits = TS.import(script, script.Parent, "limits")
|
|
7
|
+
local DISPATCH_CUTOFF_SECONDS = _limits.DISPATCH_CUTOFF_SECONDS
|
|
8
|
+
local FINAL_PHASE_SECONDS = _limits.FINAL_PHASE_SECONDS
|
|
9
|
+
local SETTLE_MARGIN_SECONDS = _limits.SETTLE_MARGIN_SECONDS
|
|
10
|
+
local SHUTDOWN_FALLBACK_SECONDS = _limits.SHUTDOWN_FALLBACK_SECONDS
|
|
11
|
+
local function pending(engine)
|
|
12
|
+
for _, runtime in engine.profiles do
|
|
13
|
+
for _1, session in runtime.sessions do
|
|
14
|
+
if session.phase ~= 3 then
|
|
15
|
+
return true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
return false
|
|
20
|
+
end
|
|
21
|
+
--[[
|
|
22
|
+
*
|
|
23
|
+
* @internal DataRuntime's onShutdown (rules 50 to 56), against core's deadline through E-D1. `subsystem` is the hook's
|
|
24
|
+
* own Subsystem, excluded from `pending`; with no visible sweep the window is SHUTDOWN_FALLBACK_SECONDS.
|
|
25
|
+
|
|
26
|
+
]]
|
|
27
|
+
local function shutdownData(engine, subsystem)
|
|
28
|
+
local port = engine.port
|
|
29
|
+
local progress = if subsystem == nil then nil else shutdownProgress(subsystem)
|
|
30
|
+
local _result = progress
|
|
31
|
+
if _result ~= nil then
|
|
32
|
+
_result = _result.deadline
|
|
33
|
+
end
|
|
34
|
+
local _condition = _result
|
|
35
|
+
if _condition == nil then
|
|
36
|
+
_condition = bootClock() + SHUTDOWN_FALLBACK_SECONDS
|
|
37
|
+
end
|
|
38
|
+
local deadline = _condition
|
|
39
|
+
local finalAt = deadline - FINAL_PHASE_SECONDS
|
|
40
|
+
local cutoff = deadline - DISPATCH_CUTOFF_SECONDS
|
|
41
|
+
local settleAt = deadline - SETTLE_MARGIN_SECONDS
|
|
42
|
+
engine.scheduler:setDispatchCutoff(cutoff)
|
|
43
|
+
engine.closing = true
|
|
44
|
+
engine.receipts:close()
|
|
45
|
+
for _, runtime in engine.profiles do
|
|
46
|
+
for _1, session in runtime.sessions do
|
|
47
|
+
if session.phase == 0 then
|
|
48
|
+
session.abandoned = true
|
|
49
|
+
elseif session.phase == 1 and session.revision > session.confirmed then
|
|
50
|
+
runtime:flush(session)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
engine:wake()
|
|
55
|
+
local others = function()
|
|
56
|
+
local _result_1
|
|
57
|
+
if subsystem == nil then
|
|
58
|
+
_result_1 = 0
|
|
59
|
+
else
|
|
60
|
+
local _result_2 = shutdownProgress(subsystem)
|
|
61
|
+
if _result_2 ~= nil then
|
|
62
|
+
_result_2 = _result_2.pending
|
|
63
|
+
end
|
|
64
|
+
local _condition_1 = _result_2
|
|
65
|
+
if _condition_1 == nil then
|
|
66
|
+
_condition_1 = 0
|
|
67
|
+
end
|
|
68
|
+
_result_1 = _condition_1
|
|
69
|
+
end
|
|
70
|
+
return _result_1
|
|
71
|
+
end
|
|
72
|
+
while bootClock() < finalAt and others() > 0 do
|
|
73
|
+
port.wait(0)
|
|
74
|
+
end
|
|
75
|
+
engine.finalPhase = true
|
|
76
|
+
for _, runtime in engine.profiles do
|
|
77
|
+
for _1, session in runtime.sessions do
|
|
78
|
+
if session.phase == 1 then
|
|
79
|
+
runtime:release(session, "shutdown")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
while bootClock() < settleAt and pending(engine) do
|
|
84
|
+
port.wait(0)
|
|
85
|
+
end
|
|
86
|
+
engine.settled = true
|
|
87
|
+
engine:wake()
|
|
88
|
+
end
|
|
89
|
+
return {
|
|
90
|
+
shutdownData = shutdownData,
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local bootClock = TS.import(script, script.Parent.Parent, "boot").bootClock
|
|
4
|
+
--[[
|
|
5
|
+
*
|
|
6
|
+
* @internal Threads parked until a wake or their deadline. Callers re-check their condition in a loop, so a spurious
|
|
7
|
+
* wake costs one check. A park id guards each resume, so a stale deadline timer never resumes a later park.
|
|
8
|
+
|
|
9
|
+
]]
|
|
10
|
+
local Waiters
|
|
11
|
+
do
|
|
12
|
+
Waiters = setmetatable({}, {
|
|
13
|
+
__tostring = function()
|
|
14
|
+
return "Waiters"
|
|
15
|
+
end,
|
|
16
|
+
})
|
|
17
|
+
Waiters.__index = Waiters
|
|
18
|
+
function Waiters.new(...)
|
|
19
|
+
local self = setmetatable({}, Waiters)
|
|
20
|
+
return self:constructor(...) or self
|
|
21
|
+
end
|
|
22
|
+
function Waiters:constructor(port)
|
|
23
|
+
self.port = port
|
|
24
|
+
self.parked = {}
|
|
25
|
+
self.sequence = 0
|
|
26
|
+
end
|
|
27
|
+
function Waiters:park(deadline)
|
|
28
|
+
local thread = coroutine.running()
|
|
29
|
+
self.sequence += 1
|
|
30
|
+
local id = self.sequence
|
|
31
|
+
self.parked[thread] = id
|
|
32
|
+
local fired = false
|
|
33
|
+
local timer = if deadline == nil then nil else self.port.delay(math.max(deadline - bootClock(), 0), function()
|
|
34
|
+
fired = true
|
|
35
|
+
if self.parked[thread] ~= id then
|
|
36
|
+
return nil
|
|
37
|
+
end
|
|
38
|
+
self.parked[thread] = nil
|
|
39
|
+
self.port.resume(thread)
|
|
40
|
+
end)
|
|
41
|
+
coroutine.yield()
|
|
42
|
+
self.parked[thread] = nil
|
|
43
|
+
if timer ~= nil and not fired then
|
|
44
|
+
self.port.cancel(timer)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
function Waiters:wake()
|
|
48
|
+
local threads = {}
|
|
49
|
+
for thread, id in self.parked do
|
|
50
|
+
local _arg0 = { thread, id }
|
|
51
|
+
table.insert(threads, _arg0)
|
|
52
|
+
end
|
|
53
|
+
table.clear(self.parked)
|
|
54
|
+
table.sort(threads, function(a, b)
|
|
55
|
+
return a[2] < b[2]
|
|
56
|
+
end)
|
|
57
|
+
for _, _binding in threads do
|
|
58
|
+
local thread = _binding[1]
|
|
59
|
+
self.port.resume(thread)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
return {
|
|
64
|
+
Waiters = Waiters,
|
|
65
|
+
}
|
|
@@ -73,6 +73,11 @@ local function stateOf(object)
|
|
|
73
73
|
local _object = object
|
|
74
74
|
return states[_object]
|
|
75
75
|
end
|
|
76
|
+
local enablingNow
|
|
77
|
+
--* @internal The generation whose Enable step is running. What it creates belongs to its enable interval.
|
|
78
|
+
local function enabling()
|
|
79
|
+
return enablingNow
|
|
80
|
+
end
|
|
76
81
|
--* @internal Awake, not faulted, and not asked to go. `get` and queries see only live objects.
|
|
77
82
|
local function isLive(s)
|
|
78
83
|
return s.fault == nil and s.want ~= 2 and (s.phase == 2 or s.phase == 3 or s.phase == 4)
|
|
@@ -168,6 +173,10 @@ local function release(s)
|
|
|
168
173
|
local _object = s.object
|
|
169
174
|
states[_object] = nil
|
|
170
175
|
end
|
|
176
|
+
local disabled = effects().disabled
|
|
177
|
+
if disabled ~= nil then
|
|
178
|
+
pcall(disabled, s)
|
|
179
|
+
end
|
|
171
180
|
local subscriptions = s.subscriptions
|
|
172
181
|
s.subscriptions = nil
|
|
173
182
|
if subscriptions == nil then
|
|
@@ -247,7 +256,10 @@ local function perform(s, step)
|
|
|
247
256
|
end
|
|
248
257
|
if step == 2 then
|
|
249
258
|
s.phase = 3
|
|
259
|
+
local previous = enablingNow
|
|
260
|
+
enablingNow = s
|
|
250
261
|
run(s, 2)
|
|
262
|
+
enablingNow = previous
|
|
251
263
|
return nil
|
|
252
264
|
end
|
|
253
265
|
if step == 3 then
|
|
@@ -289,6 +301,10 @@ local function perform(s, step)
|
|
|
289
301
|
if step == 8 then
|
|
290
302
|
s.phase = 4
|
|
291
303
|
run(s, 4)
|
|
304
|
+
local _result = fx.disabled
|
|
305
|
+
if _result ~= nil then
|
|
306
|
+
_result(s)
|
|
307
|
+
end
|
|
292
308
|
return nil
|
|
293
309
|
end
|
|
294
310
|
if step == 9 then
|
|
@@ -386,6 +402,13 @@ do
|
|
|
386
402
|
function Reconciler:isDraining()
|
|
387
403
|
return self.depth > 0
|
|
388
404
|
end
|
|
405
|
+
function Reconciler:open()
|
|
406
|
+
self.depth += 1
|
|
407
|
+
end
|
|
408
|
+
function Reconciler:leave()
|
|
409
|
+
self.depth -= 1
|
|
410
|
+
return self.depth == 0 and (#self.stack > 0 or #self.fresh > 0 or self.pairsQueued)
|
|
411
|
+
end
|
|
389
412
|
function Reconciler:enter(callback, a, b, owner)
|
|
390
413
|
self.depth += 1
|
|
391
414
|
local result, value = call(callback, a, b, owner)
|
|
@@ -760,6 +783,20 @@ local reconciler = Reconciler.new()
|
|
|
760
783
|
local function isDraining()
|
|
761
784
|
return reconciler:isDraining()
|
|
762
785
|
end
|
|
786
|
+
--[[
|
|
787
|
+
*
|
|
788
|
+
* @internal
|
|
789
|
+
* Opens a scope like `within` for a caller that runs its own worker: requests made until `closeScope` wait for it.
|
|
790
|
+
* Pair the two, with nothing between them that can throw.
|
|
791
|
+
|
|
792
|
+
]]
|
|
793
|
+
local function openScope()
|
|
794
|
+
reconciler:open()
|
|
795
|
+
end
|
|
796
|
+
--* @internal Closes the scope `openScope` opened. True when it was the outermost and requests wait: call `drain`.
|
|
797
|
+
local function closeScope()
|
|
798
|
+
return reconciler:leave()
|
|
799
|
+
end
|
|
763
800
|
--[[
|
|
764
801
|
*
|
|
765
802
|
* @internal
|
|
@@ -841,15 +878,22 @@ end
|
|
|
841
878
|
local function abandon()
|
|
842
879
|
reconciler:abandon()
|
|
843
880
|
end
|
|
881
|
+
--* @internal Forwards to the installed effects' `defer`, so networking defers ClientReady cleanups as lifecycle does.
|
|
882
|
+
local function defer(callback)
|
|
883
|
+
effects().defer(callback)
|
|
884
|
+
end
|
|
844
885
|
return {
|
|
845
886
|
nextStep = nextStep,
|
|
846
887
|
useEffects = useEffects,
|
|
847
888
|
stateOf = stateOf,
|
|
889
|
+
enabling = enabling,
|
|
848
890
|
isLive = isLive,
|
|
849
891
|
hasEnded = hasEnded,
|
|
850
892
|
track = track,
|
|
851
893
|
current = current,
|
|
852
894
|
isDraining = isDraining,
|
|
895
|
+
openScope = openScope,
|
|
896
|
+
closeScope = closeScope,
|
|
853
897
|
drain = drain,
|
|
854
898
|
notePairs = notePairs,
|
|
855
899
|
within = within,
|
|
@@ -858,6 +902,7 @@ return {
|
|
|
858
902
|
finishStart = finishStart,
|
|
859
903
|
admit = admit,
|
|
860
904
|
abandon = abandon,
|
|
905
|
+
defer = defer,
|
|
861
906
|
MAX_TRANSITIONS = MAX_TRANSITIONS,
|
|
862
907
|
MAX_AWAKE_WAVES = MAX_AWAKE_WAVES,
|
|
863
908
|
MAX_BATCHES = MAX_BATCHES,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Codec } from "./codec";
|
|
2
|
+
import type { Node } from "./schema";
|
|
3
|
+
export type MessageKind = "to-server" | "to-client" | "ask-server";
|
|
4
|
+
export interface MessageSpec {
|
|
5
|
+
readonly kind: MessageKind;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly input: Node;
|
|
8
|
+
readonly output?: Node;
|
|
9
|
+
readonly timeout?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface Definition {
|
|
12
|
+
readonly kind: MessageKind;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly input: Codec;
|
|
15
|
+
readonly output?: Codec;
|
|
16
|
+
readonly timeout: number;
|
|
17
|
+
readonly index?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function define(spec: MessageSpec): Definition;
|
|
20
|
+
export declare function seal(): string;
|
|
21
|
+
export declare function adopt(catalog: string): void;
|
|
22
|
+
export declare function definitionAt(index: number): Definition | undefined;
|
|
23
|
+
export declare function definitionOf(token: unknown): Definition | undefined;
|
|
24
|
+
export declare function discardIndices(): void;
|
|
25
|
+
export declare function unpublished(): readonly Definition[];
|
|
26
|
+
export declare function catalogSize(): number;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local compile = TS.import(script, script.Parent, "codec").compile
|
|
4
|
+
local L = TS.import(script, script.Parent, "limits")
|
|
5
|
+
local definitions = {}
|
|
6
|
+
local tokens = {}
|
|
7
|
+
--* The framework's own messages: the only names under the reserved `Crucible.` segment (NET 3, rule 55).
|
|
8
|
+
local FRAMEWORK_MESSAGES = {
|
|
9
|
+
["Crucible.State.Fault"] = true,
|
|
10
|
+
["Crucible.State.Frame"] = true,
|
|
11
|
+
}
|
|
12
|
+
local names = {}
|
|
13
|
+
local indices = {}
|
|
14
|
+
local sealed = false
|
|
15
|
+
local function validName(name)
|
|
16
|
+
local _name = name
|
|
17
|
+
local _condition = type(_name) == "string"
|
|
18
|
+
if _condition then
|
|
19
|
+
_condition = #name > 0 and #name <= L.MESSAGE_NAME_MAX_BYTES
|
|
20
|
+
end
|
|
21
|
+
assert(_condition, "invalid message name")
|
|
22
|
+
local segments = string.split(name, ".")
|
|
23
|
+
local _condition_1 = segments[1] ~= "Crucible"
|
|
24
|
+
if not _condition_1 then
|
|
25
|
+
local _name_1 = name
|
|
26
|
+
_condition_1 = FRAMEWORK_MESSAGES[_name_1] ~= nil
|
|
27
|
+
end
|
|
28
|
+
assert(_condition_1, "reserved message name")
|
|
29
|
+
for _, segment in segments do
|
|
30
|
+
local _arg0 = (string.match(segment, "^[A-Za-z][A-Za-z0-9_]*$")) ~= nil
|
|
31
|
+
assert(_arg0, "invalid message name")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
local register
|
|
35
|
+
local function define(spec)
|
|
36
|
+
validName(spec.name)
|
|
37
|
+
local _name = spec.name
|
|
38
|
+
local _arg0 = not (FRAMEWORK_MESSAGES[_name] ~= nil)
|
|
39
|
+
assert(_arg0, "reserved message name")
|
|
40
|
+
return register(spec)
|
|
41
|
+
end
|
|
42
|
+
--* @internal The trusted path: a framework message under the reserved `Crucible.` segment that game names refuse.
|
|
43
|
+
local function defineFramework(spec)
|
|
44
|
+
local _name = spec.name
|
|
45
|
+
local _arg0 = FRAMEWORK_MESSAGES[_name] ~= nil
|
|
46
|
+
assert(_arg0, "not a framework message")
|
|
47
|
+
return register(spec)
|
|
48
|
+
end
|
|
49
|
+
function register(spec)
|
|
50
|
+
local _arg0 = not sealed
|
|
51
|
+
local _arg1 = `{spec.name} was declared after the network catalog sealed; declare messages in modules that boot loads.`
|
|
52
|
+
assert(_arg0, _arg1)
|
|
53
|
+
local _name = spec.name
|
|
54
|
+
local _arg0_1 = not (definitions[_name] ~= nil)
|
|
55
|
+
local _arg1_1 = `duplicate message {spec.name}`
|
|
56
|
+
assert(_arg0_1, _arg1_1)
|
|
57
|
+
local _arg0_2 = spec.kind == "to-server" or spec.kind == "to-client" or spec.kind == "ask-server"
|
|
58
|
+
assert(_arg0_2, "invalid message kind")
|
|
59
|
+
local _arg0_3 = if spec.kind == "ask-server" then spec.output ~= nil else spec.output == nil and spec.timeout == nil
|
|
60
|
+
assert(_arg0_3, "invalid message options")
|
|
61
|
+
local _condition = spec.timeout
|
|
62
|
+
if _condition == nil then
|
|
63
|
+
_condition = L.DEFAULT_ASK_TIMEOUT_SECONDS
|
|
64
|
+
end
|
|
65
|
+
local timeout = _condition
|
|
66
|
+
local _arg0_4 = timeout > 0 and timeout <= L.MAX_ASK_TIMEOUT_SECONDS
|
|
67
|
+
assert(_arg0_4, "invalid ask timeout")
|
|
68
|
+
local tally = {
|
|
69
|
+
nodes = 0,
|
|
70
|
+
}
|
|
71
|
+
local definition = table.freeze(setmetatable({
|
|
72
|
+
kind = spec.kind,
|
|
73
|
+
name = spec.name,
|
|
74
|
+
input = compile(spec.input, tally),
|
|
75
|
+
output = if spec.output == nil then nil else compile(spec.output, tally),
|
|
76
|
+
timeout = timeout,
|
|
77
|
+
}, {
|
|
78
|
+
__index = function(_, key)
|
|
79
|
+
local _result
|
|
80
|
+
if key == "index" then
|
|
81
|
+
local _indices = indices
|
|
82
|
+
local _name_1 = spec.name
|
|
83
|
+
_result = _indices[_name_1]
|
|
84
|
+
else
|
|
85
|
+
_result = nil
|
|
86
|
+
end
|
|
87
|
+
return _result
|
|
88
|
+
end,
|
|
89
|
+
}))
|
|
90
|
+
local _name_1 = spec.name
|
|
91
|
+
definitions[_name_1] = definition
|
|
92
|
+
tokens[definition] = true
|
|
93
|
+
return definition
|
|
94
|
+
end
|
|
95
|
+
local function setNames(ordered)
|
|
96
|
+
names = ordered
|
|
97
|
+
indices = {}
|
|
98
|
+
for i = 0, #names - 1 do
|
|
99
|
+
local _indices = indices
|
|
100
|
+
local _arg0 = names[i + 1]
|
|
101
|
+
local _i = i
|
|
102
|
+
_indices[_arg0] = _i
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
local function seal()
|
|
106
|
+
local _arg0 = not sealed
|
|
107
|
+
assert(_arg0, "catalog already sealed")
|
|
108
|
+
-- ▼ ReadonlyMap.size ▼
|
|
109
|
+
local _size = 0
|
|
110
|
+
for _ in definitions do
|
|
111
|
+
_size += 1
|
|
112
|
+
end
|
|
113
|
+
-- ▲ ReadonlyMap.size ▲
|
|
114
|
+
local _arg0_1 = _size <= L.MAX_MESSAGES
|
|
115
|
+
assert(_arg0_1, "too many messages")
|
|
116
|
+
local ordered = {}
|
|
117
|
+
for name in definitions do
|
|
118
|
+
table.insert(ordered, name)
|
|
119
|
+
end
|
|
120
|
+
table.sort(ordered)
|
|
121
|
+
setNames(ordered)
|
|
122
|
+
sealed = true
|
|
123
|
+
return table.concat(names, "\n")
|
|
124
|
+
end
|
|
125
|
+
local function adopt(catalog)
|
|
126
|
+
local _condition = not sealed
|
|
127
|
+
if _condition then
|
|
128
|
+
local _catalog = catalog
|
|
129
|
+
_condition = type(_catalog) == "string"
|
|
130
|
+
if _condition then
|
|
131
|
+
_condition = #catalog < L.MAX_MESSAGES * (L.MESSAGE_NAME_MAX_BYTES + 1)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
assert(_condition, "invalid catalog")
|
|
135
|
+
local ordered = if catalog == "" then {} else string.split(catalog, "\n")
|
|
136
|
+
local _arg0 = #ordered <= L.MAX_MESSAGES
|
|
137
|
+
assert(_arg0, "too many messages")
|
|
138
|
+
for i = 0, #ordered - 1 do
|
|
139
|
+
validName(ordered[i + 1])
|
|
140
|
+
local _arg0_1 = i == 0 or ordered[i] < ordered[i + 1]
|
|
141
|
+
assert(_arg0_1, "catalog not strictly ascending")
|
|
142
|
+
end
|
|
143
|
+
setNames(ordered)
|
|
144
|
+
end
|
|
145
|
+
local function definitionAt(index)
|
|
146
|
+
if index < 0 or index % 1 ~= 0 or index >= #names then
|
|
147
|
+
return nil
|
|
148
|
+
end
|
|
149
|
+
local _arg0 = names[index + 1]
|
|
150
|
+
return definitions[_arg0]
|
|
151
|
+
end
|
|
152
|
+
local function definitionOf(token)
|
|
153
|
+
local _token = token
|
|
154
|
+
return if tokens[_token] ~= nil then token else nil
|
|
155
|
+
end
|
|
156
|
+
local function discardIndices()
|
|
157
|
+
sealed = false
|
|
158
|
+
setNames({})
|
|
159
|
+
end
|
|
160
|
+
local function unpublished()
|
|
161
|
+
local result = {}
|
|
162
|
+
for _, definition in definitions do
|
|
163
|
+
if definition.index == nil then
|
|
164
|
+
table.insert(result, definition)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
return result
|
|
168
|
+
end
|
|
169
|
+
local function catalogSize()
|
|
170
|
+
return #names
|
|
171
|
+
end
|
|
172
|
+
return {
|
|
173
|
+
define = define,
|
|
174
|
+
defineFramework = defineFramework,
|
|
175
|
+
seal = seal,
|
|
176
|
+
adopt = adopt,
|
|
177
|
+
definitionAt = definitionAt,
|
|
178
|
+
definitionOf = definitionOf,
|
|
179
|
+
discardIndices = discardIndices,
|
|
180
|
+
unpublished = unpublished,
|
|
181
|
+
catalogSize = catalogSize,
|
|
182
|
+
}
|