@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,502 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local ClientReady = TS.import(script, script.Parent.Parent.Parent, "network").ClientReady
|
|
4
|
+
local Subsystem = TS.import(script, script.Parent.Parent.Parent, "subsystem").Subsystem
|
|
5
|
+
local enginePort = TS.import(script, script.Parent.Parent, "binding").enginePort
|
|
6
|
+
local runtimeDiagnostic = TS.import(script, script.Parent.Parent, "diagnostics").runtimeDiagnostic
|
|
7
|
+
local stateOf = TS.import(script, script.Parent.Parent, "lifecycle").stateOf
|
|
8
|
+
local claim = TS.import(script, script.Parent.Parent, "network", "claims").claim
|
|
9
|
+
local KICK_MESSAGE = TS.import(script, script.Parent.Parent, "network", "limits").KICK_MESSAGE
|
|
10
|
+
local networkPort = TS.import(script, script.Parent.Parent, "network", "port").networkPort
|
|
11
|
+
local _graph = TS.import(script, script.Parent.Parent, "reactive", "graph")
|
|
12
|
+
local createComputedNode = _graph.createComputedNode
|
|
13
|
+
local currentEpoch = _graph.currentEpoch
|
|
14
|
+
local readComputed = _graph.readComputed
|
|
15
|
+
local _limits = TS.import(script, script.Parent.Parent, "reactive", "limits")
|
|
16
|
+
local SYNC_HEARTBEAT_BYTES = _limits.SYNC_HEARTBEAT_BYTES
|
|
17
|
+
local SYNC_HEARTBEAT_PEERS = _limits.SYNC_HEARTBEAT_PEERS
|
|
18
|
+
local SYNC_HEARTBEAT_VISITS = _limits.SYNC_HEARTBEAT_VISITS
|
|
19
|
+
local SYNC_MAX_REVISION = _limits.SYNC_MAX_REVISION
|
|
20
|
+
local SYNC_PEER_VISITS = _limits.SYNC_PEER_VISITS
|
|
21
|
+
local define = TS.import(script, script.Parent.Parent, "registry").define
|
|
22
|
+
local _declarations = TS.import(script, script.Parent, "declarations")
|
|
23
|
+
local declarations = _declarations.declarations
|
|
24
|
+
local useRetired = _declarations.useRetired
|
|
25
|
+
local _frame = TS.import(script, script.Parent, "frame")
|
|
26
|
+
local frameCost = _frame.frameCost
|
|
27
|
+
local overBudget = _frame.overBudget
|
|
28
|
+
local SyncFaultMessage = _frame.SyncFaultMessage
|
|
29
|
+
local SyncFrameMessage = _frame.SyncFrameMessage
|
|
30
|
+
local _values = TS.import(script, script.Parent, "values")
|
|
31
|
+
local admit = _values.admit
|
|
32
|
+
local equal = _values.equal
|
|
33
|
+
local isOverBudget = _values.isOverBudget
|
|
34
|
+
local spend = _values.spend
|
|
35
|
+
--* @internal How ServerSync sends: exactly `Network.send` on the frame message.
|
|
36
|
+
--* What a peer accepted for one declaration: the version and frozen value of the last frame `send` took.
|
|
37
|
+
--* A retired publication fails for good, at no version a projection can reach.
|
|
38
|
+
local RETIRED = -1
|
|
39
|
+
--* One examination, touching nothing on the peer until it commits (rule 58).
|
|
40
|
+
local sender = function()
|
|
41
|
+
return false
|
|
42
|
+
end
|
|
43
|
+
local ServerSync
|
|
44
|
+
do
|
|
45
|
+
local super = Subsystem
|
|
46
|
+
ServerSync = setmetatable({}, {
|
|
47
|
+
__tostring = function()
|
|
48
|
+
return "ServerSync"
|
|
49
|
+
end,
|
|
50
|
+
__index = super,
|
|
51
|
+
})
|
|
52
|
+
ServerSync.__index = ServerSync
|
|
53
|
+
function ServerSync.new(...)
|
|
54
|
+
local self = setmetatable({}, ServerSync)
|
|
55
|
+
return self:constructor(...) or self
|
|
56
|
+
end
|
|
57
|
+
function ServerSync:constructor()
|
|
58
|
+
super.constructor(self)
|
|
59
|
+
self.peers = {}
|
|
60
|
+
self.order = {}
|
|
61
|
+
self.cursor = 0
|
|
62
|
+
self.settledEpoch = -1
|
|
63
|
+
self.incomplete = false
|
|
64
|
+
self.retiredSince = false
|
|
65
|
+
self.examining = ""
|
|
66
|
+
self.spent = 0
|
|
67
|
+
self.ready = claim(self, "ready", ClientReady, function(player)
|
|
68
|
+
return self:snapshot(player)
|
|
69
|
+
end)
|
|
70
|
+
self.fault = claim(self, "fault", SyncFaultMessage, function(player)
|
|
71
|
+
return self:faulted(player)
|
|
72
|
+
end)
|
|
73
|
+
useRetired(function(declaration)
|
|
74
|
+
return self:retired(declaration)
|
|
75
|
+
end)
|
|
76
|
+
end
|
|
77
|
+
function ServerSync:awake()
|
|
78
|
+
self.disconnect = enginePort().onFrame(5, function()
|
|
79
|
+
return self:heartbeat()
|
|
80
|
+
end)
|
|
81
|
+
local state = stateOf(self)
|
|
82
|
+
if state.subscriptions == nil then
|
|
83
|
+
state.subscriptions = {}
|
|
84
|
+
end
|
|
85
|
+
local _exp = state.subscriptions
|
|
86
|
+
table.insert(_exp, function()
|
|
87
|
+
return self:stop()
|
|
88
|
+
end)
|
|
89
|
+
end
|
|
90
|
+
function ServerSync:snapshot(player)
|
|
91
|
+
local _exp = declarations()
|
|
92
|
+
-- ▼ ReadonlyArray.some ▼
|
|
93
|
+
local _result = false
|
|
94
|
+
local _callback = function(declaration)
|
|
95
|
+
return declaration.publication ~= nil
|
|
96
|
+
end
|
|
97
|
+
for _k, _v in _exp do
|
|
98
|
+
if _callback(_v, _k - 1, _exp) then
|
|
99
|
+
_result = true
|
|
100
|
+
break
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
-- ▲ ReadonlyArray.some ▲
|
|
104
|
+
if not _result then
|
|
105
|
+
return nil
|
|
106
|
+
end
|
|
107
|
+
local view = {
|
|
108
|
+
player = player,
|
|
109
|
+
revision = 0,
|
|
110
|
+
live = true,
|
|
111
|
+
accepted = {},
|
|
112
|
+
projections = {},
|
|
113
|
+
failing = {},
|
|
114
|
+
reported = {},
|
|
115
|
+
}
|
|
116
|
+
local _peers = self.peers
|
|
117
|
+
local _player = player
|
|
118
|
+
_peers[_player] = view
|
|
119
|
+
local _exp_1 = self.order
|
|
120
|
+
table.insert(_exp_1, view)
|
|
121
|
+
for _, declaration in declarations() do
|
|
122
|
+
local publication = declaration.publication
|
|
123
|
+
if declaration.audience ~= "player" or publication == nil or not publication.live then
|
|
124
|
+
continue
|
|
125
|
+
end
|
|
126
|
+
local project = publication.project
|
|
127
|
+
local _projections = view.projections
|
|
128
|
+
local _arg1 = createComputedNode(function()
|
|
129
|
+
return project(player)
|
|
130
|
+
end)
|
|
131
|
+
_projections[declaration] = _arg1
|
|
132
|
+
end
|
|
133
|
+
local work = {
|
|
134
|
+
visits = 0,
|
|
135
|
+
limit = SYNC_PEER_VISITS,
|
|
136
|
+
}
|
|
137
|
+
local staged = self:examine(view, work)
|
|
138
|
+
if staged ~= nil then
|
|
139
|
+
self:send(view, staged, "snapshot")
|
|
140
|
+
end
|
|
141
|
+
return function()
|
|
142
|
+
return self["end"](self, view)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
function ServerSync:heartbeat()
|
|
146
|
+
local ok, err = pcall(function()
|
|
147
|
+
return self:pace()
|
|
148
|
+
end)
|
|
149
|
+
if not ok then
|
|
150
|
+
runtimeDiagnostic("error", `Crucible sync: internal error in the Heartbeat: {tostring(err)}`)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
function ServerSync:pace()
|
|
154
|
+
self.spent = 0
|
|
155
|
+
local _array = {}
|
|
156
|
+
local _length = #_array
|
|
157
|
+
local _array_1 = self.order
|
|
158
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
159
|
+
local ring = _array
|
|
160
|
+
if #ring == 0 then
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
if not self.incomplete and not self.retiredSince and currentEpoch() == self.settledEpoch then
|
|
164
|
+
return nil
|
|
165
|
+
end
|
|
166
|
+
local startEpoch = currentEpoch()
|
|
167
|
+
self.retiredSince = false
|
|
168
|
+
local bytes = 0
|
|
169
|
+
local visits = 0
|
|
170
|
+
local examined = 0
|
|
171
|
+
local stopped
|
|
172
|
+
local first = self.cursor % #ring
|
|
173
|
+
for i = 0, #ring - 1 do
|
|
174
|
+
local view = ring[(first + i) % #ring + 1]
|
|
175
|
+
if not view.live then
|
|
176
|
+
continue
|
|
177
|
+
end
|
|
178
|
+
if examined > 0 and (examined >= SYNC_HEARTBEAT_PEERS or visits >= SYNC_HEARTBEAT_VISITS) then
|
|
179
|
+
stopped = view
|
|
180
|
+
break
|
|
181
|
+
end
|
|
182
|
+
local work = {
|
|
183
|
+
visits = 0,
|
|
184
|
+
limit = SYNC_PEER_VISITS,
|
|
185
|
+
}
|
|
186
|
+
local staged = self:examine(view, work)
|
|
187
|
+
visits += work.visits
|
|
188
|
+
examined += 1
|
|
189
|
+
if staged == nil then
|
|
190
|
+
continue
|
|
191
|
+
end
|
|
192
|
+
if #staged.values == 0 and #staged.failed == 0 then
|
|
193
|
+
self:commit(view, staged, false)
|
|
194
|
+
continue
|
|
195
|
+
end
|
|
196
|
+
local result = self:send(view, staged, "update", bytes)
|
|
197
|
+
if result == nil then
|
|
198
|
+
stopped = view
|
|
199
|
+
break
|
|
200
|
+
end
|
|
201
|
+
bytes += result
|
|
202
|
+
end
|
|
203
|
+
self.spent = visits
|
|
204
|
+
if stopped ~= nil then
|
|
205
|
+
self.incomplete = true
|
|
206
|
+
local _order = self.order
|
|
207
|
+
local _stopped = stopped
|
|
208
|
+
self.cursor = math.max((table.find(_order, _stopped) or 0) - 1, 0)
|
|
209
|
+
else
|
|
210
|
+
self.incomplete = false
|
|
211
|
+
self.cursor = 0
|
|
212
|
+
self.settledEpoch = startEpoch
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
function ServerSync:examine(view, work)
|
|
216
|
+
local ok, result = pcall(function()
|
|
217
|
+
return self:stage(view, work)
|
|
218
|
+
end)
|
|
219
|
+
if ok then
|
|
220
|
+
return result
|
|
221
|
+
end
|
|
222
|
+
self:fail(view, if isOverBudget(result) then `examination over SYNC_PEER_VISITS after {work.visits} visits, at {self.examining}` else `internal error at {self.examining}: {tostring(result)}`)
|
|
223
|
+
return nil
|
|
224
|
+
end
|
|
225
|
+
function ServerSync:stage(view, work)
|
|
226
|
+
local staged = {
|
|
227
|
+
values = {},
|
|
228
|
+
failed = {},
|
|
229
|
+
accept = {},
|
|
230
|
+
failing = {},
|
|
231
|
+
recovered = {},
|
|
232
|
+
reasons = {},
|
|
233
|
+
}
|
|
234
|
+
for _, declaration in declarations() do
|
|
235
|
+
local publication = declaration.publication
|
|
236
|
+
if publication == nil then
|
|
237
|
+
continue
|
|
238
|
+
end
|
|
239
|
+
self.examining = declaration.name
|
|
240
|
+
spend(work)
|
|
241
|
+
local failedAt = view.failing[declaration]
|
|
242
|
+
if not publication.live then
|
|
243
|
+
if failedAt == nil then
|
|
244
|
+
local _failed = staged.failed
|
|
245
|
+
local _name = declaration.name
|
|
246
|
+
table.insert(_failed, _name)
|
|
247
|
+
staged.failing[declaration] = RETIRED
|
|
248
|
+
end
|
|
249
|
+
continue
|
|
250
|
+
end
|
|
251
|
+
local accepted = view.accepted[declaration]
|
|
252
|
+
local version
|
|
253
|
+
local value
|
|
254
|
+
if declaration.audience == "all" then
|
|
255
|
+
version = declaration.node.version
|
|
256
|
+
value = declaration.node.value
|
|
257
|
+
if accepted ~= nil and accepted.version == version then
|
|
258
|
+
continue
|
|
259
|
+
end
|
|
260
|
+
else
|
|
261
|
+
local node = view.projections[declaration]
|
|
262
|
+
local pulled, raw = pcall(readComputed, node)
|
|
263
|
+
version = node.version
|
|
264
|
+
if failedAt == version then
|
|
265
|
+
continue
|
|
266
|
+
end
|
|
267
|
+
if failedAt == nil and accepted ~= nil and accepted.version == version then
|
|
268
|
+
continue
|
|
269
|
+
end
|
|
270
|
+
local reason
|
|
271
|
+
if pulled then
|
|
272
|
+
local admitted, result = pcall(admit, declaration.plan, declaration.name, raw, work)
|
|
273
|
+
if admitted then
|
|
274
|
+
value = result
|
|
275
|
+
elseif isOverBudget(result) then
|
|
276
|
+
error(result)
|
|
277
|
+
else
|
|
278
|
+
reason = tostring(result)
|
|
279
|
+
end
|
|
280
|
+
else
|
|
281
|
+
reason = tostring(raw)
|
|
282
|
+
end
|
|
283
|
+
if reason ~= nil then
|
|
284
|
+
if failedAt == nil then
|
|
285
|
+
local _failed = staged.failed
|
|
286
|
+
local _name = declaration.name
|
|
287
|
+
table.insert(_failed, _name)
|
|
288
|
+
local _reasons = staged.reasons
|
|
289
|
+
local _reason = reason
|
|
290
|
+
_reasons[declaration] = _reason
|
|
291
|
+
end
|
|
292
|
+
local _failing = staged.failing
|
|
293
|
+
local _version = version
|
|
294
|
+
_failing[declaration] = _version
|
|
295
|
+
continue
|
|
296
|
+
end
|
|
297
|
+
if failedAt ~= nil then
|
|
298
|
+
local _values_1 = staged.values
|
|
299
|
+
local _arg0 = {
|
|
300
|
+
key = declaration.name,
|
|
301
|
+
value = value,
|
|
302
|
+
}
|
|
303
|
+
table.insert(_values_1, _arg0)
|
|
304
|
+
local _accept = staged.accept
|
|
305
|
+
local _arg1 = {
|
|
306
|
+
version = version,
|
|
307
|
+
value = value,
|
|
308
|
+
}
|
|
309
|
+
_accept[declaration] = _arg1
|
|
310
|
+
local _exp = staged.recovered
|
|
311
|
+
table.insert(_exp, declaration)
|
|
312
|
+
continue
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
if accepted ~= nil and equal(accepted.value, value, work) then
|
|
316
|
+
local _accept = staged.accept
|
|
317
|
+
local _arg1 = {
|
|
318
|
+
version = version,
|
|
319
|
+
value = accepted.value,
|
|
320
|
+
}
|
|
321
|
+
_accept[declaration] = _arg1
|
|
322
|
+
else
|
|
323
|
+
local _values_1 = staged.values
|
|
324
|
+
local _arg0 = {
|
|
325
|
+
key = declaration.name,
|
|
326
|
+
value = value,
|
|
327
|
+
}
|
|
328
|
+
table.insert(_values_1, _arg0)
|
|
329
|
+
local _accept = staged.accept
|
|
330
|
+
local _arg1 = {
|
|
331
|
+
version = version,
|
|
332
|
+
value = value,
|
|
333
|
+
}
|
|
334
|
+
_accept[declaration] = _arg1
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
self.examining = ""
|
|
338
|
+
return staged
|
|
339
|
+
end
|
|
340
|
+
function ServerSync:send(view, staged, kind, spent)
|
|
341
|
+
if spent == nil then
|
|
342
|
+
spent = 0
|
|
343
|
+
end
|
|
344
|
+
local _exp = staged.values
|
|
345
|
+
-- ▼ ReadonlyArray.map ▼
|
|
346
|
+
local _newValue = table.create(#_exp)
|
|
347
|
+
local _callback = function(entry)
|
|
348
|
+
return entry.key
|
|
349
|
+
end
|
|
350
|
+
for _k, _v in _exp do
|
|
351
|
+
_newValue[_k] = _callback(_v, _k - 1, _exp)
|
|
352
|
+
end
|
|
353
|
+
-- ▲ ReadonlyArray.map ▲
|
|
354
|
+
local names = _newValue
|
|
355
|
+
if view.revision >= SYNC_MAX_REVISION then
|
|
356
|
+
self:fail(view, `revision passes SYNC_MAX_REVISION ({table.concat(names, ", ")})`)
|
|
357
|
+
return 0
|
|
358
|
+
end
|
|
359
|
+
local frame = {
|
|
360
|
+
failed = staged.failed,
|
|
361
|
+
kind = kind,
|
|
362
|
+
revision = view.revision + 1,
|
|
363
|
+
values = staged.values,
|
|
364
|
+
}
|
|
365
|
+
local cost = frameCost(frame, SyncFrameMessage.index)
|
|
366
|
+
local budget = overBudget(cost)
|
|
367
|
+
if budget ~= nil then
|
|
368
|
+
self:fail(view, `frame over {budget} ({cost.bytes} bytes, {cost.entries} entries, {cost.visits} visits; {table.concat(names, ", ")})`)
|
|
369
|
+
return 0
|
|
370
|
+
end
|
|
371
|
+
if spent > 0 and spent + cost.bytes > SYNC_HEARTBEAT_BYTES then
|
|
372
|
+
return nil
|
|
373
|
+
end
|
|
374
|
+
local ok, accepted = pcall(sender, view.player, frame)
|
|
375
|
+
if not ok then
|
|
376
|
+
self:fail(view, `encode refused a preflighted frame ({cost.bytes} bytes; {table.concat(names, ", ")})`)
|
|
377
|
+
return 0
|
|
378
|
+
end
|
|
379
|
+
if not accepted then
|
|
380
|
+
self["end"](self, view)
|
|
381
|
+
return 0
|
|
382
|
+
end
|
|
383
|
+
self:commit(view, staged, true)
|
|
384
|
+
return cost.bytes
|
|
385
|
+
end
|
|
386
|
+
function ServerSync:commit(view, staged, sent)
|
|
387
|
+
if sent then
|
|
388
|
+
view.revision += 1
|
|
389
|
+
end
|
|
390
|
+
for declaration, accepted in staged.accept do
|
|
391
|
+
view.accepted[declaration] = accepted
|
|
392
|
+
end
|
|
393
|
+
for declaration, version in staged.failing do
|
|
394
|
+
view.failing[declaration] = version
|
|
395
|
+
end
|
|
396
|
+
for _, declaration in staged.recovered do
|
|
397
|
+
view.failing[declaration] = nil
|
|
398
|
+
end
|
|
399
|
+
for declaration, reason in staged.reasons do
|
|
400
|
+
if view.reported[declaration] ~= nil then
|
|
401
|
+
continue
|
|
402
|
+
end
|
|
403
|
+
view.reported[declaration] = true
|
|
404
|
+
runtimeDiagnostic("error", `Crucible sync: {declaration.name} for {view.player.Name} failed: {reason}`)
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
function ServerSync:fail(view, reason)
|
|
408
|
+
if not view.live then
|
|
409
|
+
return nil
|
|
410
|
+
end
|
|
411
|
+
local player = view.player
|
|
412
|
+
pcall(sender, player, {
|
|
413
|
+
failed = {},
|
|
414
|
+
kind = "failed",
|
|
415
|
+
revision = view.revision,
|
|
416
|
+
values = {},
|
|
417
|
+
})
|
|
418
|
+
self["end"](self, view)
|
|
419
|
+
runtimeDiagnostic("error", `Crucible sync for {player.Name} failed at revision {view.revision}: {reason}; kicking`)
|
|
420
|
+
pcall(function()
|
|
421
|
+
return networkPort().kick(player, KICK_MESSAGE)
|
|
422
|
+
end)
|
|
423
|
+
end
|
|
424
|
+
function ServerSync:faulted(player)
|
|
425
|
+
local _peers = self.peers
|
|
426
|
+
local _player = player
|
|
427
|
+
local view = _peers[_player]
|
|
428
|
+
if view ~= nil and view.live then
|
|
429
|
+
self["end"](self, view)
|
|
430
|
+
runtimeDiagnostic("error", `Crucible sync for {player.Name} stopped on the client after revision {view.revision}; kicking`)
|
|
431
|
+
end
|
|
432
|
+
pcall(function()
|
|
433
|
+
return networkPort().kick(player, KICK_MESSAGE)
|
|
434
|
+
end)
|
|
435
|
+
end
|
|
436
|
+
ServerSync["end"] = function(self, view)
|
|
437
|
+
if not view.live then
|
|
438
|
+
return nil
|
|
439
|
+
end
|
|
440
|
+
view.live = false
|
|
441
|
+
table.clear(view.projections)
|
|
442
|
+
local _peers = self.peers
|
|
443
|
+
local _player = view.player
|
|
444
|
+
if _peers[_player] == view then
|
|
445
|
+
local _peers_1 = self.peers
|
|
446
|
+
local _player_1 = view.player
|
|
447
|
+
_peers_1[_player_1] = nil
|
|
448
|
+
end
|
|
449
|
+
local _order = self.order
|
|
450
|
+
local _view = view
|
|
451
|
+
local index = (table.find(_order, _view) or 0) - 1
|
|
452
|
+
if index ~= -1 then
|
|
453
|
+
table.remove(self.order, index + 1)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
function ServerSync:retired(declaration)
|
|
457
|
+
self.retiredSince = true
|
|
458
|
+
for _, view in self.order do
|
|
459
|
+
local _projections = view.projections
|
|
460
|
+
local _declaration = declaration
|
|
461
|
+
_projections[_declaration] = nil
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
function ServerSync:stop()
|
|
465
|
+
local _result = self.disconnect
|
|
466
|
+
if _result ~= nil then
|
|
467
|
+
_result()
|
|
468
|
+
end
|
|
469
|
+
self.disconnect = nil
|
|
470
|
+
local _array = {}
|
|
471
|
+
local _length = #_array
|
|
472
|
+
local _array_1 = self.order
|
|
473
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
474
|
+
for _, view in _array do
|
|
475
|
+
pcall(sender, view.player, {
|
|
476
|
+
failed = {},
|
|
477
|
+
kind = "failed",
|
|
478
|
+
revision = view.revision,
|
|
479
|
+
values = {},
|
|
480
|
+
})
|
|
481
|
+
self["end"](self, view)
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
local installed = false
|
|
486
|
+
--* @internal Rule 56: the server entry defines ServerSync once per realm, constructed before every game Subsystem.
|
|
487
|
+
local function installServerSync(send)
|
|
488
|
+
sender = send
|
|
489
|
+
if installed then
|
|
490
|
+
return nil
|
|
491
|
+
end
|
|
492
|
+
installed = true
|
|
493
|
+
define(ServerSync, {
|
|
494
|
+
kind = "subsystem",
|
|
495
|
+
id = "Crucible.ServerSync",
|
|
496
|
+
deps = {},
|
|
497
|
+
provides = {},
|
|
498
|
+
}, true)
|
|
499
|
+
end
|
|
500
|
+
return {
|
|
501
|
+
installServerSync = installServerSync,
|
|
502
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|