@trioforge/crucible 0.0.0-stage → 0.2.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/client/package.json +4 -0
- package/emit/package.json +4 -0
- package/out/all.d.ts +7 -0
- package/out/all.luau +2 -0
- package/out/attribute.d.ts +7 -0
- package/out/attribute.luau +7 -0
- package/out/client/index.d.ts +10 -0
- package/out/client/init.luau +55 -0
- package/out/configs.d.ts +37 -0
- package/out/configs.luau +46 -0
- package/out/crucible-behaviour.d.ts +16 -0
- package/out/crucible-behaviour.luau +78 -0
- package/out/crucible-object.d.ts +10 -0
- package/out/crucible-object.luau +69 -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 +45 -0
- package/out/emit/init.luau +101 -0
- 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 +20 -0
- package/out/init.luau +34 -0
- package/out/inject.d.ts +1 -0
- package/out/inject.luau +7 -0
- package/out/internal/binding.d.ts +1 -0
- package/out/internal/binding.luau +615 -0
- package/out/internal/boot.d.ts +1 -0
- package/out/internal/boot.luau +562 -0
- 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/construction-claim.d.ts +1 -0
- package/out/internal/construction-claim.luau +26 -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/dense-list.d.ts +1 -0
- package/out/internal/dense-list.luau +155 -0
- package/out/internal/diagnostics.d.ts +1 -0
- package/out/internal/diagnostics.luau +29 -0
- package/out/internal/hooks.d.ts +1 -0
- package/out/internal/hooks.luau +53 -0
- package/out/internal/implementers.d.ts +1 -0
- package/out/internal/implementers.luau +119 -0
- package/out/internal/lifecycle.d.ts +1 -0
- package/out/internal/lifecycle.luau +912 -0
- package/out/internal/log-output.d.ts +1 -0
- package/out/internal/log-output.luau +106 -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 +154 -0
- package/out/internal/network/client-session.d.ts +1 -0
- package/out/internal/network/client-session.luau +403 -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 +393 -0
- package/out/internal/network/session.d.ts +1 -0
- package/out/internal/network/session.luau +266 -0
- package/out/internal/port.d.ts +1 -0
- package/out/internal/port.luau +12 -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.d.ts +1 -0
- package/out/internal/registry.luau +455 -0
- package/out/internal/roblox-port.d.ts +1 -0
- package/out/internal/roblox-port.luau +117 -0
- package/out/internal/runner.d.ts +1 -0
- package/out/internal/runner.luau +86 -0
- package/out/internal/scheduler.d.ts +1 -0
- package/out/internal/scheduler.luau +207 -0
- package/out/internal/state.d.ts +1 -0
- package/out/internal/state.luau +41 -0
- 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/log-level.d.ts +7 -0
- package/out/log-level.luau +21 -0
- package/out/logging.d.ts +34 -0
- package/out/logging.luau +233 -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 +18 -0
- package/out/server/init.luau +62 -0
- package/out/server/publish.d.ts +20 -0
- package/out/server/publish.luau +24 -0
- package/out/subsystem.d.ts +4 -0
- package/out/subsystem.luau +30 -0
- package/out/vide/index.d.ts +7 -0
- package/out/vide/init.luau +50 -0
- package/package.json +52 -5
- package/server/package.json +4 -0
- package/transformer/data.cjs +72 -0
- package/transformer/declarations.cjs +153 -0
- package/transformer/diagnostics.cjs +19 -0
- package/transformer/index.js +741 -0
- package/transformer/network.cjs +961 -0
- package/transformer/type-id.cjs +80 -0
- package/vide/package.json +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Subsystem = TS.import(script, script.Parent.Parent.Parent, "subsystem").Subsystem
|
|
4
|
+
local runtimeDiagnostic = TS.import(script, script.Parent.Parent, "diagnostics").runtimeDiagnostic
|
|
5
|
+
local _lifecycle = TS.import(script, script.Parent.Parent, "lifecycle")
|
|
6
|
+
local isLive = _lifecycle.isLive
|
|
7
|
+
local stateOf = _lifecycle.stateOf
|
|
8
|
+
local _schema = TS.import(script, script.Parent.Parent, "network", "schema")
|
|
9
|
+
local admitsNil = _schema.admitsNil
|
|
10
|
+
local assertDescriptor = _schema.assertDescriptor
|
|
11
|
+
local runtimeKind = _schema.runtimeKind
|
|
12
|
+
local _graph = TS.import(script, script.Parent.Parent, "reactive", "graph")
|
|
13
|
+
local createStateNode = _graph.createStateNode
|
|
14
|
+
local readState = _graph.readState
|
|
15
|
+
local write = _graph.write
|
|
16
|
+
local define = TS.import(script, script.Parent.Parent, "registry").define
|
|
17
|
+
local L = TS.import(script, script.Parent, "limits")
|
|
18
|
+
local configPort = TS.import(script, script.Parent, "port").configPort
|
|
19
|
+
--* One value a reader sees. Commit is its only writer.
|
|
20
|
+
--* A config value as reactive state, so a read is tracked (rule 93) and a commit wakes its readers once.
|
|
21
|
+
local KEEP = table.freeze({})
|
|
22
|
+
local declarations = {}
|
|
23
|
+
local names = {}
|
|
24
|
+
local sealer
|
|
25
|
+
local function newScope(player)
|
|
26
|
+
return {
|
|
27
|
+
player = player,
|
|
28
|
+
phase = 0,
|
|
29
|
+
diagnosed = {},
|
|
30
|
+
ready = createStateNode(false),
|
|
31
|
+
values = {},
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
local global = newScope()
|
|
35
|
+
local slots = {}
|
|
36
|
+
--* Bumped at every join, so a read for a player with no scope yet reruns once their scope exists (rule 93).
|
|
37
|
+
local roster = createStateNode(0)
|
|
38
|
+
local owner
|
|
39
|
+
local retired = false
|
|
40
|
+
local listeners = {}
|
|
41
|
+
local flight
|
|
42
|
+
local active = 0
|
|
43
|
+
local queue = {}
|
|
44
|
+
local function fail(path, reason)
|
|
45
|
+
error({
|
|
46
|
+
configFailure = true,
|
|
47
|
+
path = path,
|
|
48
|
+
reason = reason,
|
|
49
|
+
})
|
|
50
|
+
end
|
|
51
|
+
local F32_MAX = 3.4028234663852886e38
|
|
52
|
+
local UINT_MAX = {
|
|
53
|
+
u8 = 255,
|
|
54
|
+
u16 = 65_535,
|
|
55
|
+
u32 = 4_294_967_295,
|
|
56
|
+
}
|
|
57
|
+
local function admit(node, value, root)
|
|
58
|
+
local visits = 0
|
|
59
|
+
local open = {}
|
|
60
|
+
local function enter(value, path, depth)
|
|
61
|
+
visits += 1
|
|
62
|
+
if visits > L.CONFIG_ADMISSION_VISITS then
|
|
63
|
+
fail(path, `more than {L.CONFIG_ADMISSION_VISITS} values`)
|
|
64
|
+
end
|
|
65
|
+
if depth > L.CONFIG_VALUE_DEPTH then
|
|
66
|
+
fail(path, `deeper than {L.CONFIG_VALUE_DEPTH}`)
|
|
67
|
+
end
|
|
68
|
+
if getmetatable(value) ~= nil then
|
|
69
|
+
fail(path, "a table with a metatable")
|
|
70
|
+
end
|
|
71
|
+
local _value = value
|
|
72
|
+
if open[_value] ~= nil then
|
|
73
|
+
fail(path, "a cycle")
|
|
74
|
+
end
|
|
75
|
+
local _value_1 = value
|
|
76
|
+
open[_value_1] = true
|
|
77
|
+
end
|
|
78
|
+
local function finish(source, copy)
|
|
79
|
+
local _source = source
|
|
80
|
+
open[_source] = nil
|
|
81
|
+
return table.freeze(copy)
|
|
82
|
+
end
|
|
83
|
+
local function scalar(value, path)
|
|
84
|
+
visits += 1
|
|
85
|
+
if visits > L.CONFIG_ADMISSION_VISITS then
|
|
86
|
+
fail(path, `more than {L.CONFIG_ADMISSION_VISITS} values`)
|
|
87
|
+
end
|
|
88
|
+
local _value = value
|
|
89
|
+
if type(_value) == "number" then
|
|
90
|
+
if value ~= value or math.abs(value) == math.huge then
|
|
91
|
+
fail(path, "expected a finite number")
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
local _value_1 = value
|
|
95
|
+
if type(_value_1) == "string" then
|
|
96
|
+
if #value > L.CONFIG_STRING_MAX_BYTES then
|
|
97
|
+
fail(path, `a string over {L.CONFIG_STRING_MAX_BYTES} bytes`)
|
|
98
|
+
end
|
|
99
|
+
if (utf8.len(value)) == nil then
|
|
100
|
+
fail(path, "invalid UTF-8")
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
local _value_2 = value
|
|
104
|
+
if not (type(_value_2) == "boolean") then
|
|
105
|
+
local _exp = path
|
|
106
|
+
local _value_3 = value
|
|
107
|
+
fail(_exp, `expected JSON, got {typeof(_value_3)}`)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
return value
|
|
112
|
+
end
|
|
113
|
+
local function isArray(value)
|
|
114
|
+
local array = value
|
|
115
|
+
local count = 0
|
|
116
|
+
for key in pairs(array) do
|
|
117
|
+
if not (type(key) == "number") or key % 1 ~= 0 or key < 1 or key > #array then
|
|
118
|
+
return false
|
|
119
|
+
end
|
|
120
|
+
count += 1
|
|
121
|
+
end
|
|
122
|
+
return count == #array
|
|
123
|
+
end
|
|
124
|
+
local function json(value, path, depth)
|
|
125
|
+
local _value = value
|
|
126
|
+
if not (type(_value) == "table") then
|
|
127
|
+
return scalar(value, path)
|
|
128
|
+
end
|
|
129
|
+
enter(value, path, depth)
|
|
130
|
+
local copy = {}
|
|
131
|
+
if isArray(value) then
|
|
132
|
+
local array = value
|
|
133
|
+
for i = 0, #array - 1 do
|
|
134
|
+
local _arg0 = i + 1
|
|
135
|
+
local _arg1 = json(array[i + 1], `{path}[{i}]`, depth + 1)
|
|
136
|
+
copy[_arg0] = _arg1
|
|
137
|
+
end
|
|
138
|
+
else
|
|
139
|
+
for key, child in pairs(value) do
|
|
140
|
+
if not (type(key) == "string") then
|
|
141
|
+
fail(path, "a table mixing array and object keys")
|
|
142
|
+
end
|
|
143
|
+
local _arg1 = json(child, `{path}.{key}`, depth + 1)
|
|
144
|
+
copy[key] = _arg1
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
return finish(value, copy)
|
|
148
|
+
end
|
|
149
|
+
local function tableAt(value, path, depth)
|
|
150
|
+
local _value = value
|
|
151
|
+
if not (type(_value) == "table") then
|
|
152
|
+
local _exp = path
|
|
153
|
+
local _value_1 = value
|
|
154
|
+
fail(_exp, `expected a table, got {typeof(_value_1)}`)
|
|
155
|
+
end
|
|
156
|
+
enter(value, path, depth)
|
|
157
|
+
return value
|
|
158
|
+
end
|
|
159
|
+
local function dense(value, path, depth)
|
|
160
|
+
local t = tableAt(value, path, depth)
|
|
161
|
+
if not isArray(t) then
|
|
162
|
+
fail(path, "expected an array")
|
|
163
|
+
end
|
|
164
|
+
return t
|
|
165
|
+
end
|
|
166
|
+
local function walk(node, value, path, depth)
|
|
167
|
+
local _exp = node.kind
|
|
168
|
+
repeat
|
|
169
|
+
local _fallthrough = false
|
|
170
|
+
if _exp == "nil" then
|
|
171
|
+
if value ~= nil then
|
|
172
|
+
fail(path, "expected nothing")
|
|
173
|
+
end
|
|
174
|
+
return value
|
|
175
|
+
end
|
|
176
|
+
if _exp == "literal" then
|
|
177
|
+
if value ~= node.value then
|
|
178
|
+
fail(path, `expected the literal {tostring(node.value)}`)
|
|
179
|
+
end
|
|
180
|
+
return scalar(value, path)
|
|
181
|
+
end
|
|
182
|
+
if _exp == "bool" then
|
|
183
|
+
local _value = value
|
|
184
|
+
if not (type(_value) == "boolean") then
|
|
185
|
+
local _exp_1 = path
|
|
186
|
+
local _value_1 = value
|
|
187
|
+
fail(_exp_1, `expected a boolean, got {typeof(_value_1)}`)
|
|
188
|
+
end
|
|
189
|
+
return scalar(value, path)
|
|
190
|
+
end
|
|
191
|
+
if _exp == "string" then
|
|
192
|
+
local _value = value
|
|
193
|
+
if not (type(_value) == "string") then
|
|
194
|
+
local _exp_1 = path
|
|
195
|
+
local _value_1 = value
|
|
196
|
+
fail(_exp_1, `expected a string, got {typeof(_value_1)}`)
|
|
197
|
+
end
|
|
198
|
+
return scalar(value, path)
|
|
199
|
+
end
|
|
200
|
+
if _exp == "f64" then
|
|
201
|
+
_fallthrough = true
|
|
202
|
+
end
|
|
203
|
+
if _fallthrough or _exp == "f32" then
|
|
204
|
+
_fallthrough = true
|
|
205
|
+
end
|
|
206
|
+
if _fallthrough or _exp == "u8" then
|
|
207
|
+
_fallthrough = true
|
|
208
|
+
end
|
|
209
|
+
if _fallthrough or _exp == "u16" then
|
|
210
|
+
_fallthrough = true
|
|
211
|
+
end
|
|
212
|
+
if _fallthrough or _exp == "u32" then
|
|
213
|
+
local _value = value
|
|
214
|
+
if not (type(_value) == "number") then
|
|
215
|
+
local _exp_1 = path
|
|
216
|
+
local _value_1 = value
|
|
217
|
+
fail(_exp_1, `expected a number, got {typeof(_value_1)}`)
|
|
218
|
+
end
|
|
219
|
+
scalar(value, path)
|
|
220
|
+
if node.kind == "f32" and math.abs(value) > F32_MAX then
|
|
221
|
+
fail(path, "out of f32 range")
|
|
222
|
+
end
|
|
223
|
+
if node.kind ~= "f64" and node.kind ~= "f32" then
|
|
224
|
+
local maximum = UINT_MAX[node.kind]
|
|
225
|
+
if value % 1 ~= 0 or value < 0 or value > maximum then
|
|
226
|
+
fail(path, `expected an integer in [0, {maximum}]`)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
return value
|
|
230
|
+
end
|
|
231
|
+
if _exp == "object" then
|
|
232
|
+
local source = tableAt(value, path, depth)
|
|
233
|
+
local copy = {}
|
|
234
|
+
local declared = {}
|
|
235
|
+
for _, _binding in node.fields do
|
|
236
|
+
local name = _binding[1]
|
|
237
|
+
local child = _binding[2]
|
|
238
|
+
declared[name] = true
|
|
239
|
+
local field = source[name]
|
|
240
|
+
if field ~= nil then
|
|
241
|
+
local _arg1 = walk(child, field, `{path}.{name}`, depth + 1)
|
|
242
|
+
copy[name] = _arg1
|
|
243
|
+
elseif not admitsNil(child) then
|
|
244
|
+
fail(`{path}.{name}`, "a required field is missing")
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
for key, child in pairs(source) do
|
|
248
|
+
if not (type(key) == "string") then
|
|
249
|
+
fail(path, "expected an object")
|
|
250
|
+
end
|
|
251
|
+
if not (declared[key] ~= nil) then
|
|
252
|
+
local _arg1 = json(child, `{path}.{key}`, depth + 1)
|
|
253
|
+
copy[key] = _arg1
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
return finish(source, copy)
|
|
257
|
+
end
|
|
258
|
+
if _exp == "array" then
|
|
259
|
+
local source = dense(value, path, depth)
|
|
260
|
+
-- ▼ ReadonlyArray.map ▼
|
|
261
|
+
local _newValue = table.create(#source)
|
|
262
|
+
local _callback = function(element, i)
|
|
263
|
+
return walk(node.element, element, `{path}[{i}]`, depth + 1)
|
|
264
|
+
end
|
|
265
|
+
for _k, _v in source do
|
|
266
|
+
_newValue[_k] = _callback(_v, _k - 1, source)
|
|
267
|
+
end
|
|
268
|
+
-- ▲ ReadonlyArray.map ▲
|
|
269
|
+
local copy = _newValue
|
|
270
|
+
return finish(source, copy)
|
|
271
|
+
end
|
|
272
|
+
if _exp == "tuple" then
|
|
273
|
+
local source = dense(value, path, depth)
|
|
274
|
+
if #source ~= #node.elements then
|
|
275
|
+
fail(path, `expected {#node.elements} elements`)
|
|
276
|
+
end
|
|
277
|
+
local _exp_1 = node.elements
|
|
278
|
+
-- ▼ ReadonlyArray.map ▼
|
|
279
|
+
local _newValue = table.create(#_exp_1)
|
|
280
|
+
local _callback = function(element, i)
|
|
281
|
+
return walk(element, source[i + 1], `{path}[{i}]`, depth + 1)
|
|
282
|
+
end
|
|
283
|
+
for _k, _v in _exp_1 do
|
|
284
|
+
_newValue[_k] = _callback(_v, _k - 1, _exp_1)
|
|
285
|
+
end
|
|
286
|
+
-- ▲ ReadonlyArray.map ▲
|
|
287
|
+
local copy = _newValue
|
|
288
|
+
return finish(source, copy)
|
|
289
|
+
end
|
|
290
|
+
if _exp == "map" then
|
|
291
|
+
local source = tableAt(value, path, depth)
|
|
292
|
+
local copy = {}
|
|
293
|
+
for key, child in pairs(source) do
|
|
294
|
+
local at = `{path}.{tostring(key)}`
|
|
295
|
+
walk(node.key, key, at, depth + 1)
|
|
296
|
+
local _arg1 = walk(node.value, child, at, depth + 1)
|
|
297
|
+
copy[key] = _arg1
|
|
298
|
+
end
|
|
299
|
+
return finish(source, copy)
|
|
300
|
+
end
|
|
301
|
+
if _exp == "union" then
|
|
302
|
+
for _, branch in node.branches do
|
|
303
|
+
if branch.kind == "variant" then
|
|
304
|
+
local _value = value
|
|
305
|
+
local _condition = type(_value) == "table"
|
|
306
|
+
if _condition then
|
|
307
|
+
_condition = value[branch.key] == branch.value
|
|
308
|
+
end
|
|
309
|
+
if _condition then
|
|
310
|
+
return walk(branch.body, value, path, depth)
|
|
311
|
+
end
|
|
312
|
+
else
|
|
313
|
+
local _result
|
|
314
|
+
if branch.kind == "literal" then
|
|
315
|
+
_result = value == branch.value
|
|
316
|
+
else
|
|
317
|
+
local _result_1
|
|
318
|
+
if branch.kind == "nil" then
|
|
319
|
+
_result_1 = value == nil
|
|
320
|
+
else
|
|
321
|
+
local _value = value
|
|
322
|
+
_result_1 = typeof(_value) == runtimeKind(branch)
|
|
323
|
+
end
|
|
324
|
+
_result = _result_1
|
|
325
|
+
end
|
|
326
|
+
if _result then
|
|
327
|
+
return walk(branch, value, path, depth)
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
local _exp_1 = path
|
|
332
|
+
local _value = value
|
|
333
|
+
return fail(_exp_1, `matches no member of the union, got {typeof(_value)}`)
|
|
334
|
+
end
|
|
335
|
+
fail(path, `a {node.kind} cannot be a config value`)
|
|
336
|
+
until true
|
|
337
|
+
end
|
|
338
|
+
return walk(node, value, root, 0)
|
|
339
|
+
end
|
|
340
|
+
local function acceptsString(node)
|
|
341
|
+
if node.kind == "union" then
|
|
342
|
+
local _exp = node.branches
|
|
343
|
+
-- ▼ ReadonlyArray.some ▼
|
|
344
|
+
local _result = false
|
|
345
|
+
for _k, _v in _exp do
|
|
346
|
+
if acceptsString(_v, _k - 1, _exp) then
|
|
347
|
+
_result = true
|
|
348
|
+
break
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
-- ▲ ReadonlyArray.some ▲
|
|
352
|
+
return _result
|
|
353
|
+
end
|
|
354
|
+
local _condition = node.kind == "string"
|
|
355
|
+
if not _condition then
|
|
356
|
+
local _condition_1 = node.kind == "literal"
|
|
357
|
+
if _condition_1 then
|
|
358
|
+
local _value = node.value
|
|
359
|
+
_condition_1 = type(_value) == "string"
|
|
360
|
+
end
|
|
361
|
+
_condition = _condition_1
|
|
362
|
+
end
|
|
363
|
+
return _condition
|
|
364
|
+
end
|
|
365
|
+
local function admitRaw(declaration, raw)
|
|
366
|
+
local port = configPort()
|
|
367
|
+
local value = raw
|
|
368
|
+
local _raw = raw
|
|
369
|
+
if type(_raw) == "string" then
|
|
370
|
+
if #raw > L.CONFIG_VALUE_MAX_BYTES then
|
|
371
|
+
fail(declaration.name, `over {L.CONFIG_VALUE_MAX_BYTES} bytes`)
|
|
372
|
+
end
|
|
373
|
+
if not acceptsString(declaration.node) then
|
|
374
|
+
local ok, decoded = pcall(function()
|
|
375
|
+
return port.decodeJson(raw)
|
|
376
|
+
end)
|
|
377
|
+
if not ok then
|
|
378
|
+
fail(declaration.name, "invalid JSON")
|
|
379
|
+
end
|
|
380
|
+
value = decoded
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
local _value = value
|
|
384
|
+
if type(_value) == "table" then
|
|
385
|
+
local ok, bytes = pcall(function()
|
|
386
|
+
return port.jsonBytes(value)
|
|
387
|
+
end)
|
|
388
|
+
if not ok then
|
|
389
|
+
fail(declaration.name, "not a JSON value")
|
|
390
|
+
end
|
|
391
|
+
if bytes > L.CONFIG_VALUE_MAX_BYTES then
|
|
392
|
+
fail(declaration.name, `over {L.CONFIG_VALUE_MAX_BYTES} bytes`)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
return admit(declaration.node, value, declaration.name)
|
|
396
|
+
end
|
|
397
|
+
local function failure(err)
|
|
398
|
+
local _err = err
|
|
399
|
+
local _condition = type(_err) == "table"
|
|
400
|
+
if _condition then
|
|
401
|
+
_condition = err.configFailure == true
|
|
402
|
+
end
|
|
403
|
+
if _condition then
|
|
404
|
+
return err
|
|
405
|
+
end
|
|
406
|
+
return {
|
|
407
|
+
configFailure = true,
|
|
408
|
+
path = "",
|
|
409
|
+
reason = "admission failed",
|
|
410
|
+
}
|
|
411
|
+
end
|
|
412
|
+
local function deepEqual(a, b)
|
|
413
|
+
if a == b then
|
|
414
|
+
return true
|
|
415
|
+
end
|
|
416
|
+
local _a = a
|
|
417
|
+
local _condition = not (type(_a) == "table")
|
|
418
|
+
if not _condition then
|
|
419
|
+
local _b = b
|
|
420
|
+
_condition = not (type(_b) == "table")
|
|
421
|
+
end
|
|
422
|
+
if _condition then
|
|
423
|
+
return false
|
|
424
|
+
end
|
|
425
|
+
for key, value in pairs(a) do
|
|
426
|
+
if not deepEqual(value, b[key]) then
|
|
427
|
+
return false
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
for key in pairs(b) do
|
|
431
|
+
if a[key] == nil then
|
|
432
|
+
return false
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
return true
|
|
436
|
+
end
|
|
437
|
+
local function validName(name)
|
|
438
|
+
local _name = name
|
|
439
|
+
local _condition = not (type(_name) == "string")
|
|
440
|
+
if not _condition then
|
|
441
|
+
_condition = #name == 0
|
|
442
|
+
end
|
|
443
|
+
if _condition then
|
|
444
|
+
return false
|
|
445
|
+
end
|
|
446
|
+
local segments = string.split(name, ".")
|
|
447
|
+
local _condition_1 = segments[1] ~= "Crucible"
|
|
448
|
+
if _condition_1 then
|
|
449
|
+
-- ▼ ReadonlyArray.every ▼
|
|
450
|
+
local _result = true
|
|
451
|
+
local _callback = function(s)
|
|
452
|
+
return (string.match(s, "^[A-Za-z][A-Za-z0-9_]*$")) ~= nil
|
|
453
|
+
end
|
|
454
|
+
for _k, _v in segments do
|
|
455
|
+
if not _callback(_v, _k - 1, segments) then
|
|
456
|
+
_result = false
|
|
457
|
+
break
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
-- ▲ ReadonlyArray.every ▲
|
|
461
|
+
_condition_1 = _result
|
|
462
|
+
end
|
|
463
|
+
return _condition_1
|
|
464
|
+
end
|
|
465
|
+
local ended
|
|
466
|
+
local function declare(name, kind, fallback, node)
|
|
467
|
+
if not validName(name) then
|
|
468
|
+
error(`Config name {tostring(name)} is invalid: use dot-separated [A-Za-z][A-Za-z0-9_]* segments, not Crucible.*`, 3)
|
|
469
|
+
end
|
|
470
|
+
if #name > L.CONFIG_NAME_MAX_BYTES then
|
|
471
|
+
error(`Config name {name} is {#name} bytes; the limit is {L.CONFIG_NAME_MAX_BYTES}`, 3)
|
|
472
|
+
end
|
|
473
|
+
local _name = name
|
|
474
|
+
if names[_name] ~= nil then
|
|
475
|
+
error(`Config {name} is declared twice`, 3)
|
|
476
|
+
end
|
|
477
|
+
if #declarations >= L.MAX_CONFIG_DECLARATIONS then
|
|
478
|
+
error(`Config {name} passes the limit of {L.MAX_CONFIG_DECLARATIONS} configs`, 3)
|
|
479
|
+
end
|
|
480
|
+
if sealer ~= nil and not ended(sealer) then
|
|
481
|
+
error(`{name} was declared after the config registry sealed; declare configs in modules that boot loads`, 3)
|
|
482
|
+
end
|
|
483
|
+
assertDescriptor(node)
|
|
484
|
+
local ok, admitted = pcall(function()
|
|
485
|
+
return admit(node, fallback, name)
|
|
486
|
+
end)
|
|
487
|
+
if not ok then
|
|
488
|
+
local f = failure(admitted)
|
|
489
|
+
error(`Config {name} has an invalid fallback at {f.path}: {f.reason}`, 3)
|
|
490
|
+
end
|
|
491
|
+
local declaration = {
|
|
492
|
+
name = name,
|
|
493
|
+
kind = kind,
|
|
494
|
+
node = node,
|
|
495
|
+
fallback = admitted,
|
|
496
|
+
global = createStateNode(admitted),
|
|
497
|
+
}
|
|
498
|
+
local _name_1 = name
|
|
499
|
+
names[_name_1] = true
|
|
500
|
+
table.insert(declarations, declaration)
|
|
501
|
+
return declaration
|
|
502
|
+
end
|
|
503
|
+
local function readGlobal(declaration)
|
|
504
|
+
return readState(declaration.global)
|
|
505
|
+
end
|
|
506
|
+
local function readPlayer(declaration, player)
|
|
507
|
+
local _player = player
|
|
508
|
+
local scope = slots[_player]
|
|
509
|
+
if scope == nil then
|
|
510
|
+
readState(roster)
|
|
511
|
+
return declaration.fallback
|
|
512
|
+
end
|
|
513
|
+
if readState(scope.ready) ~= true then
|
|
514
|
+
return readState(declaration.global)
|
|
515
|
+
end
|
|
516
|
+
local _values = scope.values
|
|
517
|
+
local _declaration = declaration
|
|
518
|
+
return readState(_values[_declaration])
|
|
519
|
+
end
|
|
520
|
+
local function ready(player)
|
|
521
|
+
if player == nil then
|
|
522
|
+
return readState(global.ready)
|
|
523
|
+
end
|
|
524
|
+
local _player = player
|
|
525
|
+
local scope = slots[_player]
|
|
526
|
+
if scope == nil then
|
|
527
|
+
readState(roster)
|
|
528
|
+
return false
|
|
529
|
+
end
|
|
530
|
+
return readState(scope.ready) == true
|
|
531
|
+
end
|
|
532
|
+
local function seal(runtime)
|
|
533
|
+
sealer = runtime
|
|
534
|
+
end
|
|
535
|
+
function ended(runtime)
|
|
536
|
+
local s = stateOf(runtime)
|
|
537
|
+
return s == nil or not isLive(s)
|
|
538
|
+
end
|
|
539
|
+
local function retire()
|
|
540
|
+
retired = true
|
|
541
|
+
for _, disconnect in listeners do
|
|
542
|
+
disconnect()
|
|
543
|
+
end
|
|
544
|
+
listeners = {}
|
|
545
|
+
local _result = global.snapshot
|
|
546
|
+
if _result ~= nil then
|
|
547
|
+
_result.disconnect()
|
|
548
|
+
end
|
|
549
|
+
for _, scope in slots do
|
|
550
|
+
local _result_1 = scope.snapshot
|
|
551
|
+
if _result_1 ~= nil then
|
|
552
|
+
_result_1.disconnect()
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
local function live()
|
|
557
|
+
return owner ~= nil and not retired
|
|
558
|
+
end
|
|
559
|
+
--* `subject` is a config name, or undefined for the snapshot request itself.
|
|
560
|
+
local function diagnose(scope, subject, reason)
|
|
561
|
+
local _condition = subject
|
|
562
|
+
if _condition == nil then
|
|
563
|
+
_condition = ""
|
|
564
|
+
end
|
|
565
|
+
local key = _condition
|
|
566
|
+
if scope.diagnosed[key] ~= nil then
|
|
567
|
+
return nil
|
|
568
|
+
end
|
|
569
|
+
scope.diagnosed[key] = true
|
|
570
|
+
local where = if scope.player == nil then "global snapshot" else `snapshot of player {scope.player.UserId}`
|
|
571
|
+
local _condition_1 = subject
|
|
572
|
+
if _condition_1 == nil then
|
|
573
|
+
_condition_1 = "request"
|
|
574
|
+
end
|
|
575
|
+
runtimeDiagnostic("warning", `Config {_condition_1} ({where}): {reason}`)
|
|
576
|
+
end
|
|
577
|
+
local function acquire()
|
|
578
|
+
if active < L.CONFIG_REQUEST_CONCURRENCY then
|
|
579
|
+
active += 1
|
|
580
|
+
return nil
|
|
581
|
+
end
|
|
582
|
+
local _arg0 = coroutine.running()
|
|
583
|
+
table.insert(queue, _arg0)
|
|
584
|
+
coroutine.yield()
|
|
585
|
+
end
|
|
586
|
+
local function release()
|
|
587
|
+
local waiting = table.remove(queue, 1)
|
|
588
|
+
if waiting ~= nil then
|
|
589
|
+
task.spawn(waiting)
|
|
590
|
+
else
|
|
591
|
+
active -= 1
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
local function abandoned(scope)
|
|
595
|
+
return scope.phase == 2 or not live()
|
|
596
|
+
end
|
|
597
|
+
--* One ConfigService request through the shared limit. Undefined when the scope is gone or configs retired.
|
|
598
|
+
local function request(scope, call)
|
|
599
|
+
acquire()
|
|
600
|
+
if abandoned(scope) then
|
|
601
|
+
release()
|
|
602
|
+
return nil
|
|
603
|
+
end
|
|
604
|
+
local ok, value = pcall(call)
|
|
605
|
+
release()
|
|
606
|
+
if abandoned(scope) then
|
|
607
|
+
return nil
|
|
608
|
+
end
|
|
609
|
+
return {
|
|
610
|
+
ok = ok,
|
|
611
|
+
value = value,
|
|
612
|
+
}
|
|
613
|
+
end
|
|
614
|
+
local function stage(scope, snapshot, first, updates)
|
|
615
|
+
scope.diagnosed = {}
|
|
616
|
+
local values = {}
|
|
617
|
+
for _, declaration in declarations do
|
|
618
|
+
if scope.player ~= nil and declaration.kind ~= "player" then
|
|
619
|
+
continue
|
|
620
|
+
end
|
|
621
|
+
local found, raw = pcall(function()
|
|
622
|
+
return snapshot.port.get(declaration.name)
|
|
623
|
+
end)
|
|
624
|
+
if not found then
|
|
625
|
+
diagnose(scope, declaration.name, "GetValue failed")
|
|
626
|
+
values[declaration] = KEEP
|
|
627
|
+
elseif raw == nil then
|
|
628
|
+
local _fallback = declaration.fallback
|
|
629
|
+
values[declaration] = _fallback
|
|
630
|
+
else
|
|
631
|
+
local ok, admitted = pcall(function()
|
|
632
|
+
return admitRaw(declaration, raw)
|
|
633
|
+
end)
|
|
634
|
+
if ok then
|
|
635
|
+
values[declaration] = admitted
|
|
636
|
+
else
|
|
637
|
+
local f = failure(admitted)
|
|
638
|
+
diagnose(scope, declaration.name, `{f.path}: {f.reason}; keeping the last good value`)
|
|
639
|
+
values[declaration] = KEEP
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
return {
|
|
644
|
+
scope = scope,
|
|
645
|
+
snapshot = snapshot,
|
|
646
|
+
first = first,
|
|
647
|
+
updates = updates,
|
|
648
|
+
values = values,
|
|
649
|
+
}
|
|
650
|
+
end
|
|
651
|
+
local function failed(snapshot)
|
|
652
|
+
local ok, value = pcall(function()
|
|
653
|
+
return snapshot.port.failed()
|
|
654
|
+
end)
|
|
655
|
+
return not ok or value == true
|
|
656
|
+
end
|
|
657
|
+
local function stale(snapshot)
|
|
658
|
+
if snapshot.updates ~= snapshot.applied or snapshot.dropped or failed(snapshot) then
|
|
659
|
+
return true
|
|
660
|
+
end
|
|
661
|
+
local ok, outdated = pcall(function()
|
|
662
|
+
return snapshot.port.outdated()
|
|
663
|
+
end)
|
|
664
|
+
return not ok or outdated == true
|
|
665
|
+
end
|
|
666
|
+
local function fetch(scope)
|
|
667
|
+
local player = scope.player
|
|
668
|
+
local port = configPort()
|
|
669
|
+
local result = request(scope, function()
|
|
670
|
+
return if player == nil then port.global() else port.forPlayer(player)
|
|
671
|
+
end)
|
|
672
|
+
if result == nil then
|
|
673
|
+
return nil
|
|
674
|
+
end
|
|
675
|
+
if not result.ok then
|
|
676
|
+
diagnose(scope, nil, `the fetch failed: {tostring(result.value)}`)
|
|
677
|
+
return nil
|
|
678
|
+
end
|
|
679
|
+
local snapshotPort = result.value
|
|
680
|
+
local snapshot
|
|
681
|
+
snapshot = {
|
|
682
|
+
port = snapshotPort,
|
|
683
|
+
updates = 0,
|
|
684
|
+
applied = 0,
|
|
685
|
+
dropped = false,
|
|
686
|
+
disconnect = snapshotPort.onUpdateAvailable(function()
|
|
687
|
+
if live() then
|
|
688
|
+
snapshot.updates += 1
|
|
689
|
+
end
|
|
690
|
+
end),
|
|
691
|
+
}
|
|
692
|
+
if failed(snapshot) then
|
|
693
|
+
snapshot.disconnect()
|
|
694
|
+
diagnose(scope, nil, "the snapshot failed to load")
|
|
695
|
+
return nil
|
|
696
|
+
end
|
|
697
|
+
return stage(scope, snapshot, true, 0)
|
|
698
|
+
end
|
|
699
|
+
local function refreshScope(scope)
|
|
700
|
+
if scope.phase == 0 then
|
|
701
|
+
return fetch(scope)
|
|
702
|
+
end
|
|
703
|
+
local snapshot = scope.snapshot
|
|
704
|
+
if scope.phase ~= 1 or snapshot == nil or not stale(snapshot) then
|
|
705
|
+
return nil
|
|
706
|
+
end
|
|
707
|
+
local updates = snapshot.updates
|
|
708
|
+
local result = request(scope, function()
|
|
709
|
+
return snapshot.port.refresh()
|
|
710
|
+
end)
|
|
711
|
+
if result == nil then
|
|
712
|
+
return nil
|
|
713
|
+
end
|
|
714
|
+
if not result.ok or failed(snapshot) then
|
|
715
|
+
diagnose(scope, nil, if result.ok then "the snapshot failed to load" else `the refresh failed: {tostring(result.value)}`)
|
|
716
|
+
return nil
|
|
717
|
+
end
|
|
718
|
+
return stage(scope, snapshot, false, updates)
|
|
719
|
+
end
|
|
720
|
+
--* Writes every changed value of every result in one pass that never yields.
|
|
721
|
+
local function commit(results)
|
|
722
|
+
local apply = live()
|
|
723
|
+
for _, result in results do
|
|
724
|
+
local scope = result.scope
|
|
725
|
+
if not apply or scope.phase == 2 or (result.first and scope.phase == 1) then
|
|
726
|
+
if result.first then
|
|
727
|
+
result.snapshot.disconnect()
|
|
728
|
+
end
|
|
729
|
+
continue
|
|
730
|
+
end
|
|
731
|
+
if result.first then
|
|
732
|
+
scope.snapshot = result.snapshot
|
|
733
|
+
scope.phase = 1
|
|
734
|
+
end
|
|
735
|
+
result.snapshot.applied = result.updates
|
|
736
|
+
result.snapshot.dropped = false
|
|
737
|
+
for declaration, value in result.values do
|
|
738
|
+
local cell
|
|
739
|
+
if scope.player == nil then
|
|
740
|
+
cell = declaration.global
|
|
741
|
+
else
|
|
742
|
+
cell = scope.values[declaration] or createStateNode(declaration.global.value)
|
|
743
|
+
local _values = scope.values
|
|
744
|
+
local _cell = cell
|
|
745
|
+
_values[declaration] = _cell
|
|
746
|
+
end
|
|
747
|
+
if value ~= KEEP and not deepEqual(cell.value, value) then
|
|
748
|
+
write(cell, value)
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
write(scope.ready, true)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
local function fetchLoop(scope)
|
|
755
|
+
local port = configPort()
|
|
756
|
+
do
|
|
757
|
+
local attempt = 0
|
|
758
|
+
local _shouldIncrement = false
|
|
759
|
+
while true do
|
|
760
|
+
if _shouldIncrement then
|
|
761
|
+
attempt += 1
|
|
762
|
+
else
|
|
763
|
+
_shouldIncrement = true
|
|
764
|
+
end
|
|
765
|
+
if attempt > 0 then
|
|
766
|
+
port.wait(port.random() * math.min(L.CONFIG_FETCH_RETRY_CAP_SECONDS, L.CONFIG_FETCH_RETRY_BASE_SECONDS * 2 ^ (attempt - 1)))
|
|
767
|
+
end
|
|
768
|
+
if scope.phase ~= 0 or not live() then
|
|
769
|
+
return nil
|
|
770
|
+
end
|
|
771
|
+
local result = fetch(scope)
|
|
772
|
+
if result ~= nil then
|
|
773
|
+
commit({ result })
|
|
774
|
+
return nil
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
local function refresh()
|
|
780
|
+
if not live() then
|
|
781
|
+
return false
|
|
782
|
+
end
|
|
783
|
+
if flight ~= nil then
|
|
784
|
+
local _waiters = flight.waiters
|
|
785
|
+
local _arg0 = coroutine.running()
|
|
786
|
+
table.insert(_waiters, _arg0)
|
|
787
|
+
return (coroutine.yield())
|
|
788
|
+
end
|
|
789
|
+
local current = {
|
|
790
|
+
waiters = {},
|
|
791
|
+
}
|
|
792
|
+
flight = current
|
|
793
|
+
local scopes = { global }
|
|
794
|
+
for _, scope in slots do
|
|
795
|
+
table.insert(scopes, scope)
|
|
796
|
+
end
|
|
797
|
+
local results = {}
|
|
798
|
+
local caller = coroutine.running()
|
|
799
|
+
local pending = #scopes
|
|
800
|
+
local closed = false
|
|
801
|
+
local parked = false
|
|
802
|
+
local close = function()
|
|
803
|
+
if closed then
|
|
804
|
+
return nil
|
|
805
|
+
end
|
|
806
|
+
closed = true
|
|
807
|
+
if parked then
|
|
808
|
+
task.spawn(caller)
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
for _, scope in scopes do
|
|
812
|
+
task.spawn(function()
|
|
813
|
+
local result = refreshScope(scope)
|
|
814
|
+
if closed then
|
|
815
|
+
local _result = result
|
|
816
|
+
if _result ~= nil then
|
|
817
|
+
_result = _result.first
|
|
818
|
+
end
|
|
819
|
+
if _result then
|
|
820
|
+
result.snapshot.disconnect()
|
|
821
|
+
elseif result ~= nil then
|
|
822
|
+
result.snapshot.dropped = true
|
|
823
|
+
end
|
|
824
|
+
return nil
|
|
825
|
+
end
|
|
826
|
+
if result ~= nil then
|
|
827
|
+
table.insert(results, result)
|
|
828
|
+
end
|
|
829
|
+
pending -= 1
|
|
830
|
+
if pending == 0 then
|
|
831
|
+
close()
|
|
832
|
+
end
|
|
833
|
+
end)
|
|
834
|
+
end
|
|
835
|
+
if not closed then
|
|
836
|
+
task.spawn(function()
|
|
837
|
+
configPort().wait(L.CONFIG_REFRESH_TIMEOUT_SECONDS)
|
|
838
|
+
close()
|
|
839
|
+
end)
|
|
840
|
+
parked = true
|
|
841
|
+
coroutine.yield()
|
|
842
|
+
end
|
|
843
|
+
commit(results)
|
|
844
|
+
local _condition = live()
|
|
845
|
+
if _condition then
|
|
846
|
+
-- ▼ ReadonlyArray.every ▼
|
|
847
|
+
local _result = true
|
|
848
|
+
local _callback = function(scope)
|
|
849
|
+
return scope.phase == 2 or (scope.phase == 1 and not stale(scope.snapshot))
|
|
850
|
+
end
|
|
851
|
+
for _k, _v in scopes do
|
|
852
|
+
if not _callback(_v, _k - 1, scopes) then
|
|
853
|
+
_result = false
|
|
854
|
+
break
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
-- ▲ ReadonlyArray.every ▲
|
|
858
|
+
_condition = _result
|
|
859
|
+
end
|
|
860
|
+
local ok = _condition
|
|
861
|
+
flight = nil
|
|
862
|
+
for _, waiter in current.waiters do
|
|
863
|
+
task.spawn(waiter, ok)
|
|
864
|
+
end
|
|
865
|
+
return ok
|
|
866
|
+
end
|
|
867
|
+
local function join(player)
|
|
868
|
+
local _condition = not live()
|
|
869
|
+
if not _condition then
|
|
870
|
+
local _player = player
|
|
871
|
+
_condition = slots[_player] ~= nil
|
|
872
|
+
end
|
|
873
|
+
if _condition then
|
|
874
|
+
return nil
|
|
875
|
+
end
|
|
876
|
+
local scope = newScope(player)
|
|
877
|
+
local _player = player
|
|
878
|
+
slots[_player] = scope
|
|
879
|
+
write(roster, (roster.value) + 1)
|
|
880
|
+
task.spawn(fetchLoop, scope)
|
|
881
|
+
end
|
|
882
|
+
local function leave(player)
|
|
883
|
+
if not live() then
|
|
884
|
+
return nil
|
|
885
|
+
end
|
|
886
|
+
local _player = player
|
|
887
|
+
local scope = slots[_player]
|
|
888
|
+
if scope == nil then
|
|
889
|
+
return nil
|
|
890
|
+
end
|
|
891
|
+
local _player_1 = player
|
|
892
|
+
slots[_player_1] = nil
|
|
893
|
+
scope.phase = 2
|
|
894
|
+
write(scope.ready, false)
|
|
895
|
+
local _result = scope.snapshot
|
|
896
|
+
if _result ~= nil then
|
|
897
|
+
_result.disconnect()
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
local function activate(runtime)
|
|
901
|
+
owner = runtime
|
|
902
|
+
retired = false
|
|
903
|
+
local _exp = stateOf(runtime)
|
|
904
|
+
if _exp.subscriptions == nil then
|
|
905
|
+
_exp.subscriptions = {}
|
|
906
|
+
end
|
|
907
|
+
local _exp_1 = (_exp.subscriptions)
|
|
908
|
+
table.insert(_exp_1, retire)
|
|
909
|
+
task.spawn(fetchLoop, global)
|
|
910
|
+
-- ▼ ReadonlyArray.some ▼
|
|
911
|
+
local _result = false
|
|
912
|
+
local _callback = function(d)
|
|
913
|
+
return d.kind == "player"
|
|
914
|
+
end
|
|
915
|
+
for _k, _v in declarations do
|
|
916
|
+
if _callback(_v, _k - 1, declarations) then
|
|
917
|
+
_result = true
|
|
918
|
+
break
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
-- ▲ ReadonlyArray.some ▲
|
|
922
|
+
if not _result then
|
|
923
|
+
return nil
|
|
924
|
+
end
|
|
925
|
+
local port = configPort()
|
|
926
|
+
local _listeners = listeners
|
|
927
|
+
local _arg0 = port.onPlayerAdded(join)
|
|
928
|
+
local _arg1 = port.onPlayerRemoving(leave)
|
|
929
|
+
-- ▼ Array.push ▼
|
|
930
|
+
table.insert(_listeners, _arg0)
|
|
931
|
+
table.insert(_listeners, _arg1)
|
|
932
|
+
-- ▲ Array.push ▲
|
|
933
|
+
for _, player in port.players() do
|
|
934
|
+
join(player)
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
local ConfigRuntime
|
|
938
|
+
do
|
|
939
|
+
local super = Subsystem
|
|
940
|
+
ConfigRuntime = setmetatable({}, {
|
|
941
|
+
__tostring = function()
|
|
942
|
+
return "ConfigRuntime"
|
|
943
|
+
end,
|
|
944
|
+
__index = super,
|
|
945
|
+
})
|
|
946
|
+
ConfigRuntime.__index = ConfigRuntime
|
|
947
|
+
function ConfigRuntime.new(...)
|
|
948
|
+
local self = setmetatable({}, ConfigRuntime)
|
|
949
|
+
return self:constructor(...) or self
|
|
950
|
+
end
|
|
951
|
+
function ConfigRuntime:constructor(...)
|
|
952
|
+
super.constructor(self, ...)
|
|
953
|
+
end
|
|
954
|
+
function ConfigRuntime:start()
|
|
955
|
+
seal(self)
|
|
956
|
+
if #declarations == 0 then
|
|
957
|
+
return nil
|
|
958
|
+
end
|
|
959
|
+
configPort().wait(0)
|
|
960
|
+
if ended(self) then
|
|
961
|
+
return nil
|
|
962
|
+
end
|
|
963
|
+
activate(self)
|
|
964
|
+
end
|
|
965
|
+
end
|
|
966
|
+
local function installConfigRuntime()
|
|
967
|
+
define(ConfigRuntime, {
|
|
968
|
+
kind = "subsystem",
|
|
969
|
+
id = "Crucible.ConfigRuntime",
|
|
970
|
+
deps = {},
|
|
971
|
+
provides = {},
|
|
972
|
+
})
|
|
973
|
+
end
|
|
974
|
+
return {
|
|
975
|
+
declare = declare,
|
|
976
|
+
readGlobal = readGlobal,
|
|
977
|
+
readPlayer = readPlayer,
|
|
978
|
+
ready = ready,
|
|
979
|
+
refresh = refresh,
|
|
980
|
+
installConfigRuntime = installConfigRuntime,
|
|
981
|
+
}
|