@trioforge/crucible 0.0.0-stage → 0.1.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 +3 -0
- package/out/client/init.luau +20 -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/emit/index.d.ts +41 -0
- package/out/emit/init.luau +85 -0
- package/out/index.d.ts +12 -0
- package/out/init.luau +11 -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 +610 -0
- package/out/internal/boot.d.ts +1 -0
- package/out/internal/boot.luau +463 -0
- package/out/internal/construction-claim.d.ts +1 -0
- package/out/internal/construction-claim.luau +26 -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 +867 -0
- package/out/internal/log-output.d.ts +1 -0
- package/out/internal/log-output.luau +106 -0
- package/out/internal/port.d.ts +1 -0
- package/out/internal/port.luau +12 -0
- package/out/internal/registry.d.ts +1 -0
- package/out/internal/registry.luau +446 -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/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/server/index.d.ts +2 -0
- package/out/server/init.luau +16 -0
- package/out/subsystem.d.ts +4 -0
- package/out/subsystem.luau +30 -0
- package/package.json +41 -5
- package/server/package.json +4 -0
- package/transformer/diagnostics.cjs +19 -0
- package/transformer/index.js +715 -0
- package/transformer/type-id.cjs +80 -0
- package/README.md +0 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--* @internal
|
|
3
|
+
--* @internal The error value of a call that yielded.
|
|
4
|
+
local YIELDED = "yielded; only start may yield"
|
|
5
|
+
local DONE = {}
|
|
6
|
+
local free = {}
|
|
7
|
+
local owners = {}
|
|
8
|
+
local function loop(work, a, b)
|
|
9
|
+
while true do
|
|
10
|
+
-- The only resumer is `call`, which always passes a function and its two arguments.
|
|
11
|
+
local result = work(a, b)
|
|
12
|
+
-- A parked worker must not keep its last arguments alive.
|
|
13
|
+
work = nil
|
|
14
|
+
a = nil
|
|
15
|
+
b = nil
|
|
16
|
+
local nextWork, nextA, nextB = coroutine.yield(DONE, result)
|
|
17
|
+
work = nextWork
|
|
18
|
+
a = nextA
|
|
19
|
+
b = nextB
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
--[[
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
* Runs every callback that must not yield: hooks other than `start`, constructors, `accepts`, and `All<T>`
|
|
26
|
+
* callbacks. A pool of parked worker coroutines (GoodSignal's free-runner trick) detects a yield, which
|
|
27
|
+
* `pcall` cannot. A worker returns to the pool after a call that returned, so steady-state calls allocate
|
|
28
|
+
* nothing. A throw kills its worker, and a yield gets its worker cancelled, so only failures allocate a new
|
|
29
|
+
* one. A callback that calls `call` again takes another worker, and the pool grows to the deepest nesting.
|
|
30
|
+
*
|
|
31
|
+
* `owner` becomes the worker's owner for the call, which is what `current()` reads. Without one the call is
|
|
32
|
+
* ownerless. Returns the callback's result on Ok, or the error (with a traceback) on Threw and Yielded.
|
|
33
|
+
|
|
34
|
+
]]
|
|
35
|
+
local function call(work, a, b, owner)
|
|
36
|
+
-- ▼ Array.pop ▼
|
|
37
|
+
local _length = #free
|
|
38
|
+
local _result = free[_length]
|
|
39
|
+
free[_length] = nil
|
|
40
|
+
-- ▲ Array.pop ▲
|
|
41
|
+
local _condition = _result
|
|
42
|
+
if _condition == nil then
|
|
43
|
+
_condition = coroutine.create(loop)
|
|
44
|
+
end
|
|
45
|
+
local worker = _condition
|
|
46
|
+
if owner ~= nil then
|
|
47
|
+
local _owner = owner
|
|
48
|
+
owners[worker] = _owner
|
|
49
|
+
end
|
|
50
|
+
local ok, marker, value = coroutine.resume(worker, work, a, b)
|
|
51
|
+
owners[worker] = nil
|
|
52
|
+
if not ok then
|
|
53
|
+
return 1, debug.traceback(worker, tostring(marker))
|
|
54
|
+
end
|
|
55
|
+
if marker ~= DONE then
|
|
56
|
+
local trace = debug.traceback(worker, YIELDED)
|
|
57
|
+
-- A failed cancel leaves the worker suspended, so it never returns to the pool.
|
|
58
|
+
local cancelled, err = pcall(task.cancel, worker)
|
|
59
|
+
return 2, if cancelled then trace else `{trace}\ncancellation failed: {err}`
|
|
60
|
+
end
|
|
61
|
+
table.insert(free, worker)
|
|
62
|
+
return 0, value
|
|
63
|
+
end
|
|
64
|
+
--* @internal The object that owns `thread`: a runner worker during an owned call, or a start thread for its life.
|
|
65
|
+
local function ownerOf(thread)
|
|
66
|
+
local _thread = thread
|
|
67
|
+
return owners[_thread]
|
|
68
|
+
end
|
|
69
|
+
--* @internal
|
|
70
|
+
local function own(thread, owner)
|
|
71
|
+
local _thread = thread
|
|
72
|
+
local _owner = owner
|
|
73
|
+
owners[_thread] = _owner
|
|
74
|
+
end
|
|
75
|
+
--* @internal
|
|
76
|
+
local function disown(thread)
|
|
77
|
+
local _thread = thread
|
|
78
|
+
owners[_thread] = nil
|
|
79
|
+
end
|
|
80
|
+
return {
|
|
81
|
+
call = call,
|
|
82
|
+
ownerOf = ownerOf,
|
|
83
|
+
own = own,
|
|
84
|
+
disown = disown,
|
|
85
|
+
YIELDED = YIELDED,
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local runtimeDiagnostic = TS.import(script, script.Parent, "diagnostics").runtimeDiagnostic
|
|
4
|
+
local DenseList = TS.import(script, script.Parent, "dense-list").DenseList
|
|
5
|
+
local within = TS.import(script, script.Parent, "lifecycle").within
|
|
6
|
+
--* @internal
|
|
7
|
+
--* @internal
|
|
8
|
+
--* @internal
|
|
9
|
+
--* @internal
|
|
10
|
+
local MAX_FIXED_STEPS = 4
|
|
11
|
+
--* @internal
|
|
12
|
+
local MAX_FRAME_SECONDS = 0.25
|
|
13
|
+
--* @internal
|
|
14
|
+
local function accumulate(carry, frameDt, step)
|
|
15
|
+
local total = carry + math.clamp(frameDt, 0, MAX_FRAME_SECONDS)
|
|
16
|
+
local whole = math.floor(total / step)
|
|
17
|
+
return math.min(whole, MAX_FIXED_STEPS), math.max(total - whole * step, 0)
|
|
18
|
+
end
|
|
19
|
+
local Lane
|
|
20
|
+
do
|
|
21
|
+
Lane = setmetatable({}, {
|
|
22
|
+
__tostring = function()
|
|
23
|
+
return "Lane"
|
|
24
|
+
end,
|
|
25
|
+
})
|
|
26
|
+
Lane.__index = Lane
|
|
27
|
+
function Lane.new(...)
|
|
28
|
+
local self = setmetatable({}, Lane)
|
|
29
|
+
return self:constructor(...) or self
|
|
30
|
+
end
|
|
31
|
+
function Lane:constructor(hook, frame)
|
|
32
|
+
self.members = DenseList.new()
|
|
33
|
+
self.dt = 0
|
|
34
|
+
local caller = function(s, dt)
|
|
35
|
+
return frame[hook](s.object, dt)
|
|
36
|
+
end
|
|
37
|
+
self.visit = function(s)
|
|
38
|
+
return within(s, s, 6, caller, s, self.dt)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
function Lane:run(dt)
|
|
42
|
+
self.dt = dt
|
|
43
|
+
self.members:forEach(self.visit)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
--* @internal
|
|
47
|
+
local Scheduler
|
|
48
|
+
do
|
|
49
|
+
Scheduler = setmetatable({}, {
|
|
50
|
+
__tostring = function()
|
|
51
|
+
return "Scheduler"
|
|
52
|
+
end,
|
|
53
|
+
})
|
|
54
|
+
Scheduler.__index = Scheduler
|
|
55
|
+
function Scheduler.new(...)
|
|
56
|
+
local self = setmetatable({}, Scheduler)
|
|
57
|
+
return self:constructor(...) or self
|
|
58
|
+
end
|
|
59
|
+
function Scheduler:constructor(port, config, frame)
|
|
60
|
+
self.port = port
|
|
61
|
+
self.config = config
|
|
62
|
+
self.frame = frame
|
|
63
|
+
self.renderLanes = {}
|
|
64
|
+
self.refusedClasses = {}
|
|
65
|
+
self.carry = 0
|
|
66
|
+
self.step = 1 / config.fixedRate
|
|
67
|
+
self.lanes = {
|
|
68
|
+
[0] = Lane.new(0, frame),
|
|
69
|
+
[1] = Lane.new(1, frame),
|
|
70
|
+
[2] = Lane.new(2, frame),
|
|
71
|
+
[3] = Lane.new(3, frame),
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
function Scheduler:add(s)
|
|
75
|
+
local record = s.record
|
|
76
|
+
local mask = record.hooks
|
|
77
|
+
if not self.config.renders and ((bit32.band(mask, (bit32.lshift(1, 0)))) ~= 0 or record.renderPriority ~= nil) then
|
|
78
|
+
local _refusedClasses = self.refusedClasses
|
|
79
|
+
local _id = record.id
|
|
80
|
+
if not (_refusedClasses[_id] ~= nil) then
|
|
81
|
+
local _refusedClasses_1 = self.refusedClasses
|
|
82
|
+
local _id_1 = record.id
|
|
83
|
+
_refusedClasses_1[_id_1] = true
|
|
84
|
+
runtimeDiagnostic("error", `Crucible server refuses preRender and renderPriority for {record.id}`)
|
|
85
|
+
end
|
|
86
|
+
return nil
|
|
87
|
+
end
|
|
88
|
+
if (bit32.band(mask, (bit32.lshift(1, 0)))) ~= 0 then
|
|
89
|
+
self:join(self:renderLaneOf(s), 0, s)
|
|
90
|
+
end
|
|
91
|
+
for _, hook in { 1, 2, 3 } do
|
|
92
|
+
if (bit32.band(mask, (bit32.lshift(1, hook)))) ~= 0 then
|
|
93
|
+
self:join(self.lanes[hook], hook, s)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
function Scheduler:remove(s)
|
|
98
|
+
local priority = (s.record).renderPriority
|
|
99
|
+
local render = if priority == nil then self.lanes[0] else self.renderLanes[priority]
|
|
100
|
+
if render ~= nil and render.members:remove(s) and render.members:size() == 0 then
|
|
101
|
+
local _result = render.disconnect
|
|
102
|
+
if _result ~= nil then
|
|
103
|
+
_result()
|
|
104
|
+
end
|
|
105
|
+
render.disconnect = nil
|
|
106
|
+
end
|
|
107
|
+
local fixed = self.lanes[1]
|
|
108
|
+
if fixed.members:remove(s) and fixed.members:size() == 0 then
|
|
109
|
+
local _result = fixed.disconnect
|
|
110
|
+
if _result ~= nil then
|
|
111
|
+
_result()
|
|
112
|
+
end
|
|
113
|
+
fixed.disconnect = nil
|
|
114
|
+
self.carry = 0
|
|
115
|
+
end
|
|
116
|
+
local update = self.lanes[2]
|
|
117
|
+
local late = self.lanes[3]
|
|
118
|
+
local changed = update.members:remove(s)
|
|
119
|
+
if (late.members:remove(s) or changed) and update.members:size() == 0 and late.members:size() == 0 then
|
|
120
|
+
local _result = self.postSimulation
|
|
121
|
+
if _result ~= nil then
|
|
122
|
+
_result()
|
|
123
|
+
end
|
|
124
|
+
self.postSimulation = nil
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
function Scheduler:renderLaneOf(s)
|
|
128
|
+
local priority = (s.record).renderPriority
|
|
129
|
+
if priority == nil then
|
|
130
|
+
return self.lanes[0]
|
|
131
|
+
end
|
|
132
|
+
local lane = self.renderLanes[priority]
|
|
133
|
+
if lane == nil then
|
|
134
|
+
lane = Lane.new(0, self.frame)
|
|
135
|
+
local _renderLanes = self.renderLanes
|
|
136
|
+
local _lane = lane
|
|
137
|
+
_renderLanes[priority] = _lane
|
|
138
|
+
end
|
|
139
|
+
return lane
|
|
140
|
+
end
|
|
141
|
+
function Scheduler:join(lane, hook, s)
|
|
142
|
+
if not lane.members:add(s) then
|
|
143
|
+
return nil
|
|
144
|
+
end
|
|
145
|
+
local port = self.port
|
|
146
|
+
repeat
|
|
147
|
+
local _fallthrough = false
|
|
148
|
+
if hook == 0 then
|
|
149
|
+
if lane.disconnect ~= nil or not self.config.renders then
|
|
150
|
+
return nil
|
|
151
|
+
end
|
|
152
|
+
local priority = (s.record).renderPriority
|
|
153
|
+
local run = function(dt)
|
|
154
|
+
return lane:run(dt)
|
|
155
|
+
end
|
|
156
|
+
lane.disconnect = if priority == nil then port.onFrame(0, run) else port.bindRenderStep(`crucible:{priority}`, priority, run)
|
|
157
|
+
return nil
|
|
158
|
+
end
|
|
159
|
+
if hook == 1 then
|
|
160
|
+
if lane.disconnect ~= nil then
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
lane.disconnect = port.onFrame(1, function(dt)
|
|
164
|
+
local steps, carry = accumulate(self.carry, dt, self.step)
|
|
165
|
+
self.carry = carry
|
|
166
|
+
do
|
|
167
|
+
local i = 0
|
|
168
|
+
local _shouldIncrement = false
|
|
169
|
+
while true do
|
|
170
|
+
if _shouldIncrement then
|
|
171
|
+
i += 1
|
|
172
|
+
else
|
|
173
|
+
_shouldIncrement = true
|
|
174
|
+
end
|
|
175
|
+
if not (i < steps) then
|
|
176
|
+
break
|
|
177
|
+
end
|
|
178
|
+
lane:run(self.step)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end)
|
|
182
|
+
return nil
|
|
183
|
+
end
|
|
184
|
+
if hook == 2 then
|
|
185
|
+
_fallthrough = true
|
|
186
|
+
end
|
|
187
|
+
if _fallthrough or hook == 3 then
|
|
188
|
+
if self.postSimulation ~= nil then
|
|
189
|
+
return nil
|
|
190
|
+
end
|
|
191
|
+
local update = self.lanes[2]
|
|
192
|
+
local late = self.lanes[3]
|
|
193
|
+
self.postSimulation = port.onFrame(2, function(dt)
|
|
194
|
+
update:run(dt)
|
|
195
|
+
late:run(dt)
|
|
196
|
+
end)
|
|
197
|
+
return nil
|
|
198
|
+
end
|
|
199
|
+
until true
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
return {
|
|
203
|
+
accumulate = accumulate,
|
|
204
|
+
MAX_FIXED_STEPS = MAX_FIXED_STEPS,
|
|
205
|
+
MAX_FRAME_SECONDS = MAX_FRAME_SECONDS,
|
|
206
|
+
Scheduler = Scheduler,
|
|
207
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--* @internal Public API rule 1: a Subsystem has `awake` and `start` but no `onEnable`, `onDisable` or `onDestroy`.
|
|
3
|
+
--* @internal What lifecycle reads from a class record. The registry's records extend it.
|
|
4
|
+
--* @internal An object the `construct` effect built. Lifecycle never reads it.
|
|
5
|
+
--[[
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
* Where one generation of an object is. A phase also records which inverse hooks are owed: from Awake on,
|
|
9
|
+
* one `onDestroy`; in Enabled, one `onDisable`. Each step enters its phase before running its hook, so a
|
|
10
|
+
* hook that throws part way still owes its inverse. Faulted and Destroyed are terminal.
|
|
11
|
+
*
|
|
12
|
+
* Unconstructed --constructor--> Constructed --awake--> Awake --onEnable--> Enabled <--onDisable / onEnable--> Disabled
|
|
13
|
+
* | | | \--(want Disabled, no hook)---------------------------^
|
|
14
|
+
* +--------Drop, no hooks-------+ +--------onDestroy (from Awake or Disabled)--> Destroyed | Faulted
|
|
15
|
+
|
|
16
|
+
]]
|
|
17
|
+
--* @internal `start` runs once per generation, the first time the object is Enabled with the gate at Start.
|
|
18
|
+
--[[
|
|
19
|
+
*
|
|
20
|
+
* @internal How far the object's batch has opened. A batch raises the gate on all its objects together, so
|
|
21
|
+
* every object is awake before any is enabled and enabled before any starts. After its batch an object sits at Start.
|
|
22
|
+
|
|
23
|
+
]]
|
|
24
|
+
--* @internal What the outside world asked for. The latest request wins; Destroyed is absorbing.
|
|
25
|
+
--* @internal Where a failure happened. The first one is the object's primary diagnostic.
|
|
26
|
+
--* @internal One atomic move. Each runs at most one hook or changes one index.
|
|
27
|
+
--* @internal Discriminates the two kinds of entry on the work stack.
|
|
28
|
+
--* @internal The first failure, with every later failure during its cleanup attached.
|
|
29
|
+
--* @internal The inputs `nextStep` reads. Lifecycle owns every field; nothing else writes them.
|
|
30
|
+
--* @internal Runtime state of one generation.
|
|
31
|
+
--* @internal
|
|
32
|
+
--* @internal New generations going through construct, awake, enable and start together.
|
|
33
|
+
--[[
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
* Everything a step does outside lifecycle state. Boot installs the real ones; specs install doubles.
|
|
37
|
+
* Lifecycle runs `construct` and every hook caller through `runner.call` with the object as owner, so a throw
|
|
38
|
+
* or a yield comes back as a failure, and `start` on its own thread.
|
|
39
|
+
|
|
40
|
+
]]
|
|
41
|
+
return nil
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local LogLevel
|
|
3
|
+
do
|
|
4
|
+
local _inverse = {}
|
|
5
|
+
LogLevel = setmetatable({}, {
|
|
6
|
+
__index = _inverse,
|
|
7
|
+
})
|
|
8
|
+
LogLevel.Debug = 0
|
|
9
|
+
_inverse[0] = "Debug"
|
|
10
|
+
LogLevel.Info = 1
|
|
11
|
+
_inverse[1] = "Info"
|
|
12
|
+
LogLevel.Warning = 2
|
|
13
|
+
_inverse[2] = "Warning"
|
|
14
|
+
LogLevel.Error = 3
|
|
15
|
+
_inverse[3] = "Error"
|
|
16
|
+
LogLevel.Off = 4
|
|
17
|
+
_inverse[4] = "Off"
|
|
18
|
+
end
|
|
19
|
+
return {
|
|
20
|
+
LogLevel = LogLevel,
|
|
21
|
+
}
|
package/out/logging.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LogLevel } from "./log-level";
|
|
2
|
+
export { LogLevel } from "./log-level";
|
|
3
|
+
export type LogContext = Readonly<Record<string, unknown>>;
|
|
4
|
+
export type LogProperties = Readonly<Record<string, string | number | boolean>>;
|
|
5
|
+
export interface LogRecord {
|
|
6
|
+
readonly sequence: number;
|
|
7
|
+
readonly time: number;
|
|
8
|
+
readonly source: string;
|
|
9
|
+
readonly level: Exclude<LogLevel, LogLevel.Off>;
|
|
10
|
+
readonly template: string;
|
|
11
|
+
readonly context: LogProperties;
|
|
12
|
+
readonly message: string;
|
|
13
|
+
readonly traceback?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface LogSink {
|
|
16
|
+
readonly write: (record: LogRecord) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface SinkSubscription {
|
|
19
|
+
setMinimum(level: LogLevel): void;
|
|
20
|
+
disconnect(): void;
|
|
21
|
+
}
|
|
22
|
+
export interface Logger {
|
|
23
|
+
isEnabled(level: Exclude<LogLevel, LogLevel.Off>): boolean;
|
|
24
|
+
debug(template: string, context?: LogContext): void;
|
|
25
|
+
info(template: string, context?: LogContext): void;
|
|
26
|
+
warn(template: string, context?: LogContext): void;
|
|
27
|
+
error(template: string, context?: LogContext): void;
|
|
28
|
+
}
|
|
29
|
+
export declare class Logging {
|
|
30
|
+
private constructor();
|
|
31
|
+
static readonly defaultSink: SinkSubscription;
|
|
32
|
+
static getLogger(source: string): Logger;
|
|
33
|
+
static addSink(sink: LogSink, minimum?: LogLevel): SinkSubscription;
|
|
34
|
+
}
|
package/out/logging.luau
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local LogLevel = TS.import(script, script.Parent, "log-level").LogLevel
|
|
5
|
+
local runtimeDiagnostic = TS.import(script, script.Parent, "internal", "diagnostics").runtimeDiagnostic
|
|
6
|
+
local loggingPort = TS.import(script, script.Parent, "internal", "log-output").loggingPort
|
|
7
|
+
local call = TS.import(script, script.Parent, "internal", "runner").call
|
|
8
|
+
exports.LogLevel = TS.import(script, script.Parent, "log-level").LogLevel
|
|
9
|
+
local registrations = {}
|
|
10
|
+
local snapshot = {}
|
|
11
|
+
local minimum = LogLevel.Off
|
|
12
|
+
local sequence = 0
|
|
13
|
+
local emitting = false
|
|
14
|
+
local function derive(added)
|
|
15
|
+
local entries = {}
|
|
16
|
+
minimum = LogLevel.Off
|
|
17
|
+
-- roblox-ts Maps compile to unordered Luau tables. The array owns delivery order.
|
|
18
|
+
for _, registration in snapshot do
|
|
19
|
+
if not registration.connected then
|
|
20
|
+
continue
|
|
21
|
+
end
|
|
22
|
+
table.insert(entries, registration)
|
|
23
|
+
minimum = math.min(minimum, registration.minimum)
|
|
24
|
+
end
|
|
25
|
+
if added ~= nil then
|
|
26
|
+
local _added = added
|
|
27
|
+
table.insert(entries, _added)
|
|
28
|
+
minimum = math.min(minimum, added.minimum)
|
|
29
|
+
end
|
|
30
|
+
snapshot = table.freeze(entries)
|
|
31
|
+
end
|
|
32
|
+
local function validateMinimum(level)
|
|
33
|
+
local _arg0 = level >= LogLevel.Debug and level <= LogLevel.Off and level % 1 == 0
|
|
34
|
+
assert(_arg0, "Invalid log minimum")
|
|
35
|
+
end
|
|
36
|
+
local function register(sink, level)
|
|
37
|
+
validateMinimum(level)
|
|
38
|
+
local _sink = sink
|
|
39
|
+
local existing = registrations[_sink]
|
|
40
|
+
if existing ~= nil then
|
|
41
|
+
return existing.subscription
|
|
42
|
+
end
|
|
43
|
+
local _write = sink.write
|
|
44
|
+
local _arg0 = type(_write) == "function"
|
|
45
|
+
assert(_arg0, "Log sink requires write")
|
|
46
|
+
local registration
|
|
47
|
+
registration = {
|
|
48
|
+
sink = sink,
|
|
49
|
+
minimum = level,
|
|
50
|
+
connected = true,
|
|
51
|
+
subscription = {
|
|
52
|
+
setMinimum = function(self, value)
|
|
53
|
+
validateMinimum(value)
|
|
54
|
+
if not registration.connected then
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
registration.minimum = value
|
|
58
|
+
derive()
|
|
59
|
+
end,
|
|
60
|
+
disconnect = function(self)
|
|
61
|
+
if not registration.connected then
|
|
62
|
+
return nil
|
|
63
|
+
end
|
|
64
|
+
registration.connected = false
|
|
65
|
+
local _sink_1 = sink
|
|
66
|
+
registrations[_sink_1] = nil
|
|
67
|
+
derive()
|
|
68
|
+
end,
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
local _sink_1 = sink
|
|
72
|
+
registrations[_sink_1] = registration
|
|
73
|
+
derive(registration)
|
|
74
|
+
return registration.subscription
|
|
75
|
+
end
|
|
76
|
+
local function normalize(input)
|
|
77
|
+
local context = {}
|
|
78
|
+
if input ~= nil then
|
|
79
|
+
for key, value in pairs(input) do
|
|
80
|
+
context[key] = if typeof(value) == "Instance" then loggingPort().describeInstance(value) elseif type(value) == "string" or type(value) == "number" or type(value) == "boolean" then value else tostring(value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
return table.freeze(context)
|
|
84
|
+
end
|
|
85
|
+
local function render(template, context)
|
|
86
|
+
local parts = {}
|
|
87
|
+
local index = 1
|
|
88
|
+
while index <= #template do
|
|
89
|
+
local _template = template
|
|
90
|
+
local _index = index
|
|
91
|
+
local _index_1 = index
|
|
92
|
+
local char = string.sub(_template, _index, _index_1)
|
|
93
|
+
local _template_1 = template
|
|
94
|
+
local _index_2 = index
|
|
95
|
+
local _arg1 = index + 1
|
|
96
|
+
local pair = string.sub(_template_1, _index_2, _arg1)
|
|
97
|
+
if pair == "{{" or pair == "}}" then
|
|
98
|
+
table.insert(parts, char)
|
|
99
|
+
index += 2
|
|
100
|
+
elseif char == "{" then
|
|
101
|
+
local _template_2 = template
|
|
102
|
+
local _arg1_1 = index + 1
|
|
103
|
+
local close = string.find(_template_2, "}", _arg1_1, true)
|
|
104
|
+
if close ~= nil then
|
|
105
|
+
local _template_3 = template
|
|
106
|
+
local _arg0 = index + 1
|
|
107
|
+
local _arg1_2 = close - 1
|
|
108
|
+
local key = string.sub(_template_3, _arg0, _arg1_2)
|
|
109
|
+
local value = context[key]
|
|
110
|
+
local _result
|
|
111
|
+
if value == nil then
|
|
112
|
+
local _template_4 = template
|
|
113
|
+
local _index_3 = index
|
|
114
|
+
_result = string.sub(_template_4, _index_3, close)
|
|
115
|
+
else
|
|
116
|
+
_result = tostring(value)
|
|
117
|
+
end
|
|
118
|
+
table.insert(parts, _result)
|
|
119
|
+
index = close + 1
|
|
120
|
+
else
|
|
121
|
+
table.insert(parts, char)
|
|
122
|
+
index += 1
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
table.insert(parts, char)
|
|
126
|
+
index += 1
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
return table.concat(parts, "")
|
|
130
|
+
end
|
|
131
|
+
local function emit(source, level, template, input)
|
|
132
|
+
if emitting or level < minimum then
|
|
133
|
+
return nil
|
|
134
|
+
end
|
|
135
|
+
emitting = true
|
|
136
|
+
local trace = if level == LogLevel.Error then debug.traceback("", 3) else nil
|
|
137
|
+
local result, failure = call(function()
|
|
138
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
139
|
+
local _newValue = {}
|
|
140
|
+
local _callback = function(r)
|
|
141
|
+
return level >= r.minimum
|
|
142
|
+
end
|
|
143
|
+
local _length = 0
|
|
144
|
+
for _k, _v in snapshot do
|
|
145
|
+
if _callback(_v, _k - 1, snapshot) == true then
|
|
146
|
+
_length += 1
|
|
147
|
+
_newValue[_length] = _v
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
151
|
+
local accepted = _newValue
|
|
152
|
+
local context = normalize(input)
|
|
153
|
+
local _object = {}
|
|
154
|
+
local _left = "sequence"
|
|
155
|
+
sequence += 1
|
|
156
|
+
_object[_left] = sequence
|
|
157
|
+
_object.time = loggingPort().now()
|
|
158
|
+
_object.source = source
|
|
159
|
+
_object.level = level
|
|
160
|
+
_object.template = template
|
|
161
|
+
_object.context = context
|
|
162
|
+
_object.message = render(template, context)
|
|
163
|
+
_object.traceback = trace
|
|
164
|
+
local record = table.freeze(_object)
|
|
165
|
+
for _, registration in accepted do
|
|
166
|
+
if not registration.connected then
|
|
167
|
+
continue
|
|
168
|
+
end
|
|
169
|
+
local status, err = call(registration.sink.write, record, nil)
|
|
170
|
+
if status ~= 0 then
|
|
171
|
+
registration.subscription:disconnect()
|
|
172
|
+
runtimeDiagnostic("error", `Log sink failed: {tostring(err)}`)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end, nil, nil)
|
|
176
|
+
if result ~= 0 then
|
|
177
|
+
runtimeDiagnostic("error", `Logging failed: {tostring(failure)}`)
|
|
178
|
+
end
|
|
179
|
+
emitting = false
|
|
180
|
+
end
|
|
181
|
+
local Logging
|
|
182
|
+
do
|
|
183
|
+
Logging = setmetatable({}, {
|
|
184
|
+
__tostring = function()
|
|
185
|
+
return "Logging"
|
|
186
|
+
end,
|
|
187
|
+
})
|
|
188
|
+
Logging.__index = Logging
|
|
189
|
+
function Logging.new(...)
|
|
190
|
+
local self = setmetatable({}, Logging)
|
|
191
|
+
return self:constructor(...) or self
|
|
192
|
+
end
|
|
193
|
+
function Logging:constructor()
|
|
194
|
+
end
|
|
195
|
+
function Logging:getLogger(source)
|
|
196
|
+
local _arg0 = #source > 0 and #source <= 256 and (string.find(source, "%c")) == nil
|
|
197
|
+
assert(_arg0, "Invalid logger name")
|
|
198
|
+
for _, segment in string.split(source, ".") do
|
|
199
|
+
local _arg0_1 = #segment > 0
|
|
200
|
+
assert(_arg0_1, "Invalid logger name segment")
|
|
201
|
+
end
|
|
202
|
+
return {
|
|
203
|
+
isEnabled = function(self, level)
|
|
204
|
+
return level >= minimum
|
|
205
|
+
end,
|
|
206
|
+
debug = function(self, template, context)
|
|
207
|
+
emit(source, LogLevel.Debug, template, context)
|
|
208
|
+
end,
|
|
209
|
+
info = function(self, template, context)
|
|
210
|
+
emit(source, LogLevel.Info, template, context)
|
|
211
|
+
end,
|
|
212
|
+
warn = function(self, template, context)
|
|
213
|
+
emit(source, LogLevel.Warning, template, context)
|
|
214
|
+
end,
|
|
215
|
+
error = function(self, template, context)
|
|
216
|
+
emit(source, LogLevel.Error, template, context)
|
|
217
|
+
end,
|
|
218
|
+
}
|
|
219
|
+
end
|
|
220
|
+
function Logging:addSink(sink, minimum)
|
|
221
|
+
if minimum == nil then
|
|
222
|
+
minimum = LogLevel.Info
|
|
223
|
+
end
|
|
224
|
+
return register(sink, minimum)
|
|
225
|
+
end
|
|
226
|
+
Logging.defaultSink = register({
|
|
227
|
+
write = function(record)
|
|
228
|
+
return loggingPort().writeGame(record)
|
|
229
|
+
end,
|
|
230
|
+
}, if loggingPort().isStudio() then LogLevel.Debug else LogLevel.Info)
|
|
231
|
+
end
|
|
232
|
+
exports.Logging = Logging
|
|
233
|
+
return exports
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local bootSide = TS.import(script, script.Parent, "internal", "boot").boot
|
|
4
|
+
if not game:GetService("RunService"):IsServer() then
|
|
5
|
+
error("@trioforge/crucible/server loaded on the client")
|
|
6
|
+
end
|
|
7
|
+
local function boot(options)
|
|
8
|
+
bootSide({
|
|
9
|
+
name = "server",
|
|
10
|
+
renders = false,
|
|
11
|
+
booted = function() end,
|
|
12
|
+
}, options)
|
|
13
|
+
end
|
|
14
|
+
return {
|
|
15
|
+
boot = boot,
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local CrucibleObject = TS.import(script, script.Parent, "crucible-object").CrucibleObject
|
|
4
|
+
local installSubsystemHooks = TS.import(script, script.Parent, "internal", "hooks").installSubsystemHooks
|
|
5
|
+
local Subsystem
|
|
6
|
+
do
|
|
7
|
+
local super = CrucibleObject
|
|
8
|
+
Subsystem = setmetatable({}, {
|
|
9
|
+
__tostring = function()
|
|
10
|
+
return "Subsystem"
|
|
11
|
+
end,
|
|
12
|
+
__index = super,
|
|
13
|
+
})
|
|
14
|
+
Subsystem.__index = Subsystem
|
|
15
|
+
function Subsystem:constructor(...)
|
|
16
|
+
super.constructor(self, ...)
|
|
17
|
+
end
|
|
18
|
+
function Subsystem:onShutdown()
|
|
19
|
+
end
|
|
20
|
+
do
|
|
21
|
+
installSubsystemHooks({
|
|
22
|
+
onShutdown = function(o)
|
|
23
|
+
return o:onShutdown()
|
|
24
|
+
end,
|
|
25
|
+
})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
return {
|
|
29
|
+
Subsystem = Subsystem,
|
|
30
|
+
}
|