@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.
Files changed (145) hide show
  1. package/maths/spline/package.json +4 -0
  2. package/out/client/index.d.ts +7 -0
  3. package/out/client/init.luau +36 -1
  4. package/out/configs.d.ts +37 -0
  5. package/out/configs.luau +46 -0
  6. package/out/data.d.ts +224 -0
  7. package/out/data.luau +97 -0
  8. package/out/emit/configs.d.ts +4 -0
  9. package/out/emit/configs.luau +22 -0
  10. package/out/emit/data.d.ts +6 -0
  11. package/out/emit/data.luau +19 -0
  12. package/out/emit/index.d.ts +5 -1
  13. package/out/emit/init.luau +23 -7
  14. package/out/emit/network.d.ts +6 -0
  15. package/out/emit/network.luau +36 -0
  16. package/out/emit/replicated.d.ts +4 -0
  17. package/out/emit/replicated.luau +13 -0
  18. package/out/index.d.ts +8 -0
  19. package/out/init.luau +23 -0
  20. package/out/internal/binding.luau +5 -0
  21. package/out/internal/boot.luau +112 -8
  22. package/out/internal/configs/limits.d.ts +10 -0
  23. package/out/internal/configs/limits.luau +25 -0
  24. package/out/internal/configs/port.d.ts +29 -0
  25. package/out/internal/configs/port.luau +99 -0
  26. package/out/internal/configs/runtime.d.ts +21 -0
  27. package/out/internal/configs/runtime.luau +981 -0
  28. package/out/internal/data/admission.d.ts +1 -0
  29. package/out/internal/data/admission.luau +673 -0
  30. package/out/internal/data/declarations.d.ts +1 -0
  31. package/out/internal/data/declarations.luau +384 -0
  32. package/out/internal/data/envelope.d.ts +1 -0
  33. package/out/internal/data/envelope.luau +318 -0
  34. package/out/internal/data/handles.d.ts +1 -0
  35. package/out/internal/data/handles.luau +255 -0
  36. package/out/internal/data/inbox-transforms.d.ts +1 -0
  37. package/out/internal/data/inbox-transforms.luau +458 -0
  38. package/out/internal/data/inbox.d.ts +1 -0
  39. package/out/internal/data/inbox.luau +437 -0
  40. package/out/internal/data/lease.d.ts +1 -0
  41. package/out/internal/data/lease.luau +304 -0
  42. package/out/internal/data/limits.d.ts +1 -0
  43. package/out/internal/data/limits.luau +234 -0
  44. package/out/internal/data/messages.d.ts +1 -0
  45. package/out/internal/data/messages.luau +69 -0
  46. package/out/internal/data/operation-id.d.ts +1 -0
  47. package/out/internal/data/operation-id.luau +61 -0
  48. package/out/internal/data/paths.d.ts +2 -0
  49. package/out/internal/data/paths.luau +323 -0
  50. package/out/internal/data/port.d.ts +1 -0
  51. package/out/internal/data/port.luau +290 -0
  52. package/out/internal/data/profiles.d.ts +1 -0
  53. package/out/internal/data/profiles.luau +862 -0
  54. package/out/internal/data/receipts.d.ts +1 -0
  55. package/out/internal/data/receipts.luau +190 -0
  56. package/out/internal/data/record-bindings.d.ts +1 -0
  57. package/out/internal/data/record-bindings.luau +92 -0
  58. package/out/internal/data/record-declarations.d.ts +1 -0
  59. package/out/internal/data/record-declarations.luau +140 -0
  60. package/out/internal/data/records.d.ts +1 -0
  61. package/out/internal/data/records.luau +400 -0
  62. package/out/internal/data/runtime.d.ts +1 -0
  63. package/out/internal/data/runtime.luau +918 -0
  64. package/out/internal/data/scheduler.d.ts +1 -0
  65. package/out/internal/data/scheduler.luau +507 -0
  66. package/out/internal/data/session.d.ts +1 -0
  67. package/out/internal/data/session.luau +234 -0
  68. package/out/internal/data/shutdown.d.ts +1 -0
  69. package/out/internal/data/shutdown.luau +91 -0
  70. package/out/internal/data/waiters.d.ts +1 -0
  71. package/out/internal/data/waiters.luau +65 -0
  72. package/out/internal/lifecycle.luau +45 -0
  73. package/out/internal/network/catalog.d.ts +26 -0
  74. package/out/internal/network/catalog.luau +182 -0
  75. package/out/internal/network/claims.d.ts +1 -0
  76. package/out/internal/network/claims.luau +146 -0
  77. package/out/internal/network/client-session.d.ts +1 -0
  78. package/out/internal/network/client-session.luau +405 -0
  79. package/out/internal/network/codec.d.ts +63 -0
  80. package/out/internal/network/codec.luau +1183 -0
  81. package/out/internal/network/envelope.d.ts +1 -0
  82. package/out/internal/network/envelope.luau +247 -0
  83. package/out/internal/network/limits.d.ts +26 -0
  84. package/out/internal/network/limits.luau +55 -0
  85. package/out/internal/network/port.d.ts +1 -0
  86. package/out/internal/network/port.luau +19 -0
  87. package/out/internal/network/roblox-network-port.d.ts +1 -0
  88. package/out/internal/network/roblox-network-port.luau +105 -0
  89. package/out/internal/network/schema.d.ts +72 -0
  90. package/out/internal/network/schema.luau +386 -0
  91. package/out/internal/network/server-session.d.ts +1 -0
  92. package/out/internal/network/server-session.luau +395 -0
  93. package/out/internal/network/session.d.ts +1 -0
  94. package/out/internal/network/session.luau +266 -0
  95. package/out/internal/reactive/context.d.ts +1 -0
  96. package/out/internal/reactive/context.luau +149 -0
  97. package/out/internal/reactive/flush.d.ts +1 -0
  98. package/out/internal/reactive/flush.luau +355 -0
  99. package/out/internal/reactive/graph.d.ts +1 -0
  100. package/out/internal/reactive/graph.luau +626 -0
  101. package/out/internal/reactive/limits.d.ts +1 -0
  102. package/out/internal/reactive/limits.luau +54 -0
  103. package/out/internal/reactive/ownership.d.ts +1 -0
  104. package/out/internal/reactive/ownership.luau +626 -0
  105. package/out/internal/registry.luau +10 -1
  106. package/out/internal/sync/client-sync.d.ts +1 -0
  107. package/out/internal/sync/client-sync.luau +157 -0
  108. package/out/internal/sync/declarations.d.ts +1 -0
  109. package/out/internal/sync/declarations.luau +276 -0
  110. package/out/internal/sync/frame.d.ts +1 -0
  111. package/out/internal/sync/frame.luau +91 -0
  112. package/out/internal/sync/server-sync.d.ts +1 -0
  113. package/out/internal/sync/server-sync.luau +502 -0
  114. package/out/internal/sync/values.d.ts +1 -0
  115. package/out/internal/sync/values.luau +930 -0
  116. package/out/maths/spline/curve.d.ts +35 -0
  117. package/out/maths/spline/curve.luau +1122 -0
  118. package/out/maths/spline/index.d.ts +2 -0
  119. package/out/maths/spline/init.luau +8 -0
  120. package/out/network.d.ts +66 -0
  121. package/out/network.luau +24 -0
  122. package/out/reactive.d.ts +76 -0
  123. package/out/reactive.luau +136 -0
  124. package/out/replicated.d.ts +38 -0
  125. package/out/replicated.luau +45 -0
  126. package/out/server/data.d.ts +54 -0
  127. package/out/server/data.luau +44 -0
  128. package/out/server/index.d.ts +17 -0
  129. package/out/server/init.luau +58 -4
  130. package/out/server/publish.d.ts +20 -0
  131. package/out/server/publish.luau +24 -0
  132. package/out/testing/index.d.ts +38 -0
  133. package/out/testing/init.luau +189 -0
  134. package/out/util/random/index.d.ts +24 -0
  135. package/out/util/random/init.luau +301 -0
  136. package/out/vide/index.d.ts +7 -0
  137. package/out/vide/init.luau +50 -0
  138. package/package.json +28 -2
  139. package/testing/package.json +4 -0
  140. package/transformer/data.cjs +72 -0
  141. package/transformer/declarations.cjs +153 -0
  142. package/transformer/index.js +87 -23
  143. package/transformer/network.cjs +961 -0
  144. package/util/random/package.json +4 -0
  145. 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 {};