roblox-opencode 1.0.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/README.md +122 -0
- package/commands/setup-game.md +108 -0
- package/commands/sync-check.md +53 -0
- package/core/roblox-core.md +93 -0
- package/dist/server.js +167 -0
- package/package.json +35 -0
- package/skills/roblox-analytics/SKILL.md +277 -0
- package/skills/roblox-analytics/references/event-batcher.luau +75 -0
- package/skills/roblox-animation-vfx/SKILL.md +1325 -0
- package/skills/roblox-architecture/SKILL.md +863 -0
- package/skills/roblox-architecture/references/combat-systems.md +1381 -0
- package/skills/roblox-code-review/SKILL.md +687 -0
- package/skills/roblox-data/SKILL.md +889 -0
- package/skills/roblox-data/references/inventory-systems.md +1729 -0
- package/skills/roblox-debug/SKILL.md +99 -0
- package/skills/roblox-gui/SKILL.md +1103 -0
- package/skills/roblox-gui-fusion/SKILL.md +150 -0
- package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
- package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
- package/skills/roblox-gui-fusion/references/shop.luau +411 -0
- package/skills/roblox-luau-mastery/SKILL.md +1519 -0
- package/skills/roblox-monetization/SKILL.md +1084 -0
- package/skills/roblox-monetization/references/process-receipt.luau +131 -0
- package/skills/roblox-networking/SKILL.md +669 -0
- package/skills/roblox-networking/references/remote-validator.luau +193 -0
- package/skills/roblox-publish-checklist/SKILL.md +128 -0
- package/skills/roblox-runtime/SKILL.md +753 -0
- package/skills/roblox-sharp-edges/SKILL.md +295 -0
- package/skills/roblox-sync/SKILL.md +126 -0
- package/skills/roblox-testing/SKILL.md +943 -0
- package/skills/roblox-tooling/SKILL.md +150 -0
- package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
- package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
- package/vendor/LICENSES/promise-LICENSE +21 -0
- package/vendor/LICENSES/t-LICENSE +21 -0
- package/vendor/LICENSES/testez-LICENSE +201 -0
- package/vendor/README.md +84 -0
- package/vendor/fusion/Animation/ExternalTime.luau +84 -0
- package/vendor/fusion/Animation/Spring.luau +322 -0
- package/vendor/fusion/Animation/Stopwatch.luau +128 -0
- package/vendor/fusion/Animation/Tween.luau +187 -0
- package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
- package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
- package/vendor/fusion/Animation/lerpType.luau +164 -0
- package/vendor/fusion/Animation/packType.luau +100 -0
- package/vendor/fusion/Animation/springCoefficients.luau +80 -0
- package/vendor/fusion/Animation/unpackType.luau +103 -0
- package/vendor/fusion/Colour/Oklab.luau +70 -0
- package/vendor/fusion/Colour/sRGB.luau +55 -0
- package/vendor/fusion/External.luau +168 -0
- package/vendor/fusion/ExternalDebug.luau +70 -0
- package/vendor/fusion/Graph/Observer.luau +114 -0
- package/vendor/fusion/Graph/castToGraph.luau +29 -0
- package/vendor/fusion/Graph/change.luau +81 -0
- package/vendor/fusion/Graph/depend.luau +33 -0
- package/vendor/fusion/Graph/evaluate.luau +56 -0
- package/vendor/fusion/Instances/Attribute.luau +58 -0
- package/vendor/fusion/Instances/AttributeChange.luau +47 -0
- package/vendor/fusion/Instances/AttributeOut.luau +63 -0
- package/vendor/fusion/Instances/Child.luau +21 -0
- package/vendor/fusion/Instances/Children.luau +148 -0
- package/vendor/fusion/Instances/Hydrate.luau +33 -0
- package/vendor/fusion/Instances/New.luau +53 -0
- package/vendor/fusion/Instances/OnChange.luau +50 -0
- package/vendor/fusion/Instances/OnEvent.luau +54 -0
- package/vendor/fusion/Instances/Out.luau +69 -0
- package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
- package/vendor/fusion/Instances/defaultProps.luau +194 -0
- package/vendor/fusion/LICENSE +21 -0
- package/vendor/fusion/Logging/formatError.luau +49 -0
- package/vendor/fusion/Logging/messages.luau +52 -0
- package/vendor/fusion/Logging/parseError.luau +25 -0
- package/vendor/fusion/Memory/checkLifetime.luau +134 -0
- package/vendor/fusion/Memory/deriveScope.luau +24 -0
- package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
- package/vendor/fusion/Memory/doCleanup.luau +79 -0
- package/vendor/fusion/Memory/innerScope.luau +34 -0
- package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
- package/vendor/fusion/Memory/needsDestruction.luau +17 -0
- package/vendor/fusion/Memory/poisonScope.luau +34 -0
- package/vendor/fusion/Memory/scopePool.luau +55 -0
- package/vendor/fusion/Memory/scoped.luau +27 -0
- package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
- package/vendor/fusion/RobloxExternal.luau +98 -0
- package/vendor/fusion/State/Computed.luau +139 -0
- package/vendor/fusion/State/For/Disassembly.luau +211 -0
- package/vendor/fusion/State/For/ForTypes.luau +30 -0
- package/vendor/fusion/State/For/init.luau +110 -0
- package/vendor/fusion/State/ForKeys.luau +94 -0
- package/vendor/fusion/State/ForPairs.luau +97 -0
- package/vendor/fusion/State/ForValues.luau +94 -0
- package/vendor/fusion/State/Value.luau +88 -0
- package/vendor/fusion/State/castToState.luau +26 -0
- package/vendor/fusion/State/peek.luau +31 -0
- package/vendor/fusion/State/updateAll.luau +1 -0
- package/vendor/fusion/Types.luau +314 -0
- package/vendor/fusion/Utility/Contextual.luau +91 -0
- package/vendor/fusion/Utility/Safe.luau +23 -0
- package/vendor/fusion/Utility/isSimilar.luau +29 -0
- package/vendor/fusion/Utility/merge.luau +35 -0
- package/vendor/fusion/Utility/nameOf.luau +35 -0
- package/vendor/fusion/Utility/never.luau +14 -0
- package/vendor/fusion/Utility/nicknames.luau +11 -0
- package/vendor/fusion/Utility/xtypeof.luau +27 -0
- package/vendor/fusion/init.luau +82 -0
- package/vendor/profilestore/init.luau +2243 -0
- package/vendor/promise/init.luau +1982 -0
- package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
- package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
- package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
- package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
- package/vendor/rbxutil/buffer-util/Types.luau +60 -0
- package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
- package/vendor/rbxutil/buffer-util/init.luau +41 -0
- package/vendor/rbxutil/buffer-util/package.json +16 -0
- package/vendor/rbxutil/buffer-util/wally.toml +9 -0
- package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
- package/vendor/rbxutil/comm/Client/init.luau +135 -0
- package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
- package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
- package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
- package/vendor/rbxutil/comm/Server/init.luau +140 -0
- package/vendor/rbxutil/comm/Types.luau +18 -0
- package/vendor/rbxutil/comm/Util.luau +27 -0
- package/vendor/rbxutil/comm/init.luau +35 -0
- package/vendor/rbxutil/comm/wally.toml +13 -0
- package/vendor/rbxutil/component/init.luau +759 -0
- package/vendor/rbxutil/component/init.test.luau +311 -0
- package/vendor/rbxutil/component/wally.toml +14 -0
- package/vendor/rbxutil/concur/init.luau +542 -0
- package/vendor/rbxutil/concur/init.test.luau +364 -0
- package/vendor/rbxutil/concur/wally.toml +8 -0
- package/vendor/rbxutil/enum-list/init.luau +101 -0
- package/vendor/rbxutil/enum-list/init.test.luau +91 -0
- package/vendor/rbxutil/enum-list/wally.toml +8 -0
- package/vendor/rbxutil/find/index.d.ts +20 -0
- package/vendor/rbxutil/find/init.luau +44 -0
- package/vendor/rbxutil/find/package.json +17 -0
- package/vendor/rbxutil/find/wally.toml +8 -0
- package/vendor/rbxutil/input/Gamepad.luau +559 -0
- package/vendor/rbxutil/input/Keyboard.luau +124 -0
- package/vendor/rbxutil/input/Mouse.luau +278 -0
- package/vendor/rbxutil/input/PreferredInput.luau +91 -0
- package/vendor/rbxutil/input/Touch.luau +120 -0
- package/vendor/rbxutil/input/init.luau +33 -0
- package/vendor/rbxutil/input/wally.toml +12 -0
- package/vendor/rbxutil/loader/index.d.ts +15 -0
- package/vendor/rbxutil/loader/init.luau +137 -0
- package/vendor/rbxutil/loader/wally.toml +8 -0
- package/vendor/rbxutil/log/index.d.ts +38 -0
- package/vendor/rbxutil/log/init.luau +746 -0
- package/vendor/rbxutil/log/wally.toml +8 -0
- package/vendor/rbxutil/net/init.luau +190 -0
- package/vendor/rbxutil/net/wally.toml +8 -0
- package/vendor/rbxutil/option/index.d.ts +44 -0
- package/vendor/rbxutil/option/init.luau +489 -0
- package/vendor/rbxutil/option/init.test.luau +342 -0
- package/vendor/rbxutil/option/wally.toml +8 -0
- package/vendor/rbxutil/pid/index.d.ts +53 -0
- package/vendor/rbxutil/pid/init.luau +195 -0
- package/vendor/rbxutil/pid/package.json +16 -0
- package/vendor/rbxutil/pid/wally.toml +9 -0
- package/vendor/rbxutil/quaternion/index.d.ts +117 -0
- package/vendor/rbxutil/quaternion/init.luau +570 -0
- package/vendor/rbxutil/quaternion/package.json +16 -0
- package/vendor/rbxutil/quaternion/wally.toml +9 -0
- package/vendor/rbxutil/query/index.d.ts +43 -0
- package/vendor/rbxutil/query/init.luau +117 -0
- package/vendor/rbxutil/query/package.json +18 -0
- package/vendor/rbxutil/query/wally.toml +9 -0
- package/vendor/rbxutil/sequent/index.d.ts +28 -0
- package/vendor/rbxutil/sequent/init.luau +340 -0
- package/vendor/rbxutil/sequent/package.json +16 -0
- package/vendor/rbxutil/sequent/wally.toml +9 -0
- package/vendor/rbxutil/ser/init.luau +175 -0
- package/vendor/rbxutil/ser/init.test.luau +50 -0
- package/vendor/rbxutil/ser/wally.toml +11 -0
- package/vendor/rbxutil/shake/index.d.ts +36 -0
- package/vendor/rbxutil/shake/init.luau +532 -0
- package/vendor/rbxutil/shake/init.test.luau +267 -0
- package/vendor/rbxutil/shake/package.json +16 -0
- package/vendor/rbxutil/shake/wally.toml +9 -0
- package/vendor/rbxutil/signal/index.d.ts +100 -0
- package/vendor/rbxutil/signal/init.luau +432 -0
- package/vendor/rbxutil/signal/init.test.luau +190 -0
- package/vendor/rbxutil/signal/package.json +17 -0
- package/vendor/rbxutil/signal/wally.toml +9 -0
- package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
- package/vendor/rbxutil/silo/Util.luau +55 -0
- package/vendor/rbxutil/silo/init.luau +338 -0
- package/vendor/rbxutil/silo/init.test.luau +215 -0
- package/vendor/rbxutil/silo/wally.toml +8 -0
- package/vendor/rbxutil/spring/index.d.ts +40 -0
- package/vendor/rbxutil/spring/init.luau +97 -0
- package/vendor/rbxutil/spring/package.json +17 -0
- package/vendor/rbxutil/spring/wally.toml +8 -0
- package/vendor/rbxutil/stream/index.d.ts +88 -0
- package/vendor/rbxutil/stream/init.luau +597 -0
- package/vendor/rbxutil/stream/package.json +18 -0
- package/vendor/rbxutil/stream/wally.toml +9 -0
- package/vendor/rbxutil/streamable/Streamable.luau +202 -0
- package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
- package/vendor/rbxutil/streamable/init.luau +8 -0
- package/vendor/rbxutil/streamable/wally.toml +12 -0
- package/vendor/rbxutil/symbol/init.luau +56 -0
- package/vendor/rbxutil/symbol/init.test.luau +37 -0
- package/vendor/rbxutil/symbol/wally.toml +8 -0
- package/vendor/rbxutil/table-util/init.luau +938 -0
- package/vendor/rbxutil/table-util/init.test.luau +439 -0
- package/vendor/rbxutil/table-util/wally.toml +8 -0
- package/vendor/rbxutil/task-queue/index.d.ts +27 -0
- package/vendor/rbxutil/task-queue/init.luau +97 -0
- package/vendor/rbxutil/task-queue/wally.toml +8 -0
- package/vendor/rbxutil/timer/index.d.ts +81 -0
- package/vendor/rbxutil/timer/init.luau +249 -0
- package/vendor/rbxutil/timer/init.test.luau +73 -0
- package/vendor/rbxutil/timer/wally.toml +11 -0
- package/vendor/rbxutil/tree/index.d.ts +15 -0
- package/vendor/rbxutil/tree/init.luau +137 -0
- package/vendor/rbxutil/tree/wally.toml +8 -0
- package/vendor/rbxutil/trove/index.d.ts +46 -0
- package/vendor/rbxutil/trove/init.luau +787 -0
- package/vendor/rbxutil/trove/init.test.luau +203 -0
- package/vendor/rbxutil/trove/wally.toml +8 -0
- package/vendor/rbxutil/typed-remote/init.luau +196 -0
- package/vendor/rbxutil/typed-remote/wally.toml +8 -0
- package/vendor/rbxutil/wait-for/index.d.ts +17 -0
- package/vendor/rbxutil/wait-for/init.luau +257 -0
- package/vendor/rbxutil/wait-for/init.test.luau +182 -0
- package/vendor/rbxutil/wait-for/wally.toml +11 -0
- package/vendor/t/t.lua +1350 -0
- package/vendor/testez/Context.lua +26 -0
- package/vendor/testez/Expectation.lua +311 -0
- package/vendor/testez/ExpectationContext.lua +38 -0
- package/vendor/testez/LifecycleHooks.lua +89 -0
- package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
- package/vendor/testez/Reporters/TextReporter.lua +106 -0
- package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
- package/vendor/testez/TestBootstrap.lua +147 -0
- package/vendor/testez/TestEnum.lua +28 -0
- package/vendor/testez/TestPlan.lua +304 -0
- package/vendor/testez/TestPlanner.lua +40 -0
- package/vendor/testez/TestResults.lua +112 -0
- package/vendor/testez/TestRunner.lua +188 -0
- package/vendor/testez/TestSession.lua +243 -0
- package/vendor/testez/init.lua +40 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Provides functions for converting Color3s into Oklab space, for more
|
|
8
|
+
perceptually uniform colour blending.
|
|
9
|
+
|
|
10
|
+
See: https://bottosson.github.io/posts/oklab/
|
|
11
|
+
]]
|
|
12
|
+
|
|
13
|
+
local sRGB = require(script.Parent.sRGB)
|
|
14
|
+
|
|
15
|
+
local Oklab = {}
|
|
16
|
+
|
|
17
|
+
-- Converts a Color3 in linear RGB space to a Vector3 in Oklab space.
|
|
18
|
+
function Oklab.fromLinear(rgb: Color3): Vector3
|
|
19
|
+
|
|
20
|
+
local l = rgb.R * 0.4122214708 + rgb.G * 0.5363325363 + rgb.B * 0.0514459929
|
|
21
|
+
local m = rgb.R * 0.2119034982 + rgb.G * 0.6806995451 + rgb.B * 0.1073969566
|
|
22
|
+
local s = rgb.R * 0.0883024619 + rgb.G * 0.2817188376 + rgb.B * 0.6299787005
|
|
23
|
+
|
|
24
|
+
local lRoot = l ^ (1/3)
|
|
25
|
+
local mRoot = m ^ (1/3)
|
|
26
|
+
local sRoot = s ^ (1/3)
|
|
27
|
+
|
|
28
|
+
return Vector3.new(
|
|
29
|
+
lRoot * 0.2104542553 + mRoot * 0.7936177850 - sRoot * 0.0040720468,
|
|
30
|
+
lRoot * 1.9779984951 - mRoot * 2.4285922050 + sRoot * 0.4505937099,
|
|
31
|
+
lRoot * 0.0259040371 + mRoot * 0.7827717662 - sRoot * 0.8086757660
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
-- Converts a Color3 in sRGB space to a Vector3 in Oklab space.
|
|
36
|
+
function Oklab.fromSRGB(srgb: Color3): Vector3
|
|
37
|
+
return Oklab.fromLinear(sRGB.toLinear(srgb))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
-- Converts a Vector3 in Oklab space to a Color3 in linear RGB space.
|
|
41
|
+
-- The Color3 will be clamped by default unless specified otherwise.
|
|
42
|
+
function Oklab.toLinear(lab: Vector3, unclamped: boolean?): Color3
|
|
43
|
+
local lRoot = lab.X + lab.Y * 0.3963377774 + lab.Z * 0.2158037573
|
|
44
|
+
local mRoot = lab.X - lab.Y * 0.1055613458 - lab.Z * 0.0638541728
|
|
45
|
+
local sRoot = lab.X - lab.Y * 0.0894841775 - lab.Z * 1.2914855480
|
|
46
|
+
|
|
47
|
+
local l = lRoot ^ 3
|
|
48
|
+
local m = mRoot ^ 3
|
|
49
|
+
local s = sRoot ^ 3
|
|
50
|
+
|
|
51
|
+
local red = l * 4.0767416621 - m * 3.3077115913 + s * 0.2309699292
|
|
52
|
+
local green = l * -1.2684380046 + m * 2.6097574011 - s * 0.3413193965
|
|
53
|
+
local blue = l * -0.0041960863 - m * 0.7034186147 + s * 1.7076147010
|
|
54
|
+
|
|
55
|
+
if not unclamped then
|
|
56
|
+
red = math.clamp(red, 0, 1)
|
|
57
|
+
green = math.clamp(green, 0, 1)
|
|
58
|
+
blue = math.clamp(blue, 0, 1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return Color3.new(red, green, blue)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
-- Converts a Vector3 in Oklab space to a Color3 in sRGB space.
|
|
65
|
+
-- The Color3 will be clamped by default unless specified otherwise.
|
|
66
|
+
function Oklab.toSRGB(lab: Vector3, unclamped: boolean?): Color3
|
|
67
|
+
return sRGB.fromLinear(Oklab.toLinear(lab, unclamped))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return Oklab
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Provides transformation functions for converting linear RGB values
|
|
8
|
+
into sRGB values.
|
|
9
|
+
|
|
10
|
+
RGB color channel transformations are outlined here:
|
|
11
|
+
https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
local sRGB = {}
|
|
15
|
+
|
|
16
|
+
-- Equivalent to f_inv. Takes a linear sRGB channel and returns
|
|
17
|
+
-- the sRGB channel
|
|
18
|
+
local function transform(channel: number): number
|
|
19
|
+
if channel >= 0.04045 then
|
|
20
|
+
return ((channel + 0.055)/(1 + 0.055))^2.4
|
|
21
|
+
else
|
|
22
|
+
return channel / 12.92
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
-- Equivalent to f. Takes an sRGB channel and returns
|
|
27
|
+
-- the linear sRGB channel
|
|
28
|
+
local function inverse(channel: number): number
|
|
29
|
+
if channel >= 0.0031308 then
|
|
30
|
+
return (1.055) * channel^(1.0/2.4) - 0.055
|
|
31
|
+
else
|
|
32
|
+
return 12.92 * channel
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
-- Uses a tranformation to convert linear RGB into sRGB.
|
|
37
|
+
function sRGB.fromLinear(rgb: Color3): Color3
|
|
38
|
+
return Color3.new(
|
|
39
|
+
transform(rgb.R),
|
|
40
|
+
transform(rgb.G),
|
|
41
|
+
transform(rgb.B)
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
-- Converts an sRGB into linear RGB using a
|
|
46
|
+
-- (The inverse of sRGB.fromLinear).
|
|
47
|
+
function sRGB.toLinear(srgb: Color3): Color3
|
|
48
|
+
return Color3.new(
|
|
49
|
+
inverse(srgb.R),
|
|
50
|
+
inverse(srgb.G),
|
|
51
|
+
inverse(srgb.B)
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
return sRGB
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Abstraction layer between Fusion internals and external environments,
|
|
8
|
+
allowing for flexible integration with schedulers and test mocks.
|
|
9
|
+
]]
|
|
10
|
+
|
|
11
|
+
local Package = script.Parent
|
|
12
|
+
local formatError = require(Package.Logging.formatError)
|
|
13
|
+
local Types = require(Package.Types)
|
|
14
|
+
|
|
15
|
+
local ERROR_INFO_URL = "https://elttob.uk/Fusion/0.3/api-reference/general/errors/#"
|
|
16
|
+
|
|
17
|
+
local External = {}
|
|
18
|
+
|
|
19
|
+
-- Indicates that a highly time-critical passage of code is running. During
|
|
20
|
+
-- critical periods of a program, Fusion might decide to change some of its
|
|
21
|
+
-- internal behaviour to be more performance friendly.
|
|
22
|
+
local timeCritical = false
|
|
23
|
+
|
|
24
|
+
-- Multiplier for running-time safety checks across the Fusion codebase. Used to
|
|
25
|
+
-- stricten tests on infinite loop detection during unit testing.
|
|
26
|
+
External.safetyTimerMultiplier = 1
|
|
27
|
+
|
|
28
|
+
local updateStepCallbacks = {}
|
|
29
|
+
local currentProvider: Types.ExternalProvider? = nil
|
|
30
|
+
local lastUpdateStep = 0
|
|
31
|
+
|
|
32
|
+
--[[
|
|
33
|
+
Swaps to a new provider for external operations.
|
|
34
|
+
Returns the old provider, so it can be used again later.
|
|
35
|
+
]]
|
|
36
|
+
function External.setExternalProvider(
|
|
37
|
+
newProvider: Types.ExternalProvider?
|
|
38
|
+
): Types.ExternalProvider?
|
|
39
|
+
local oldProvider = currentProvider
|
|
40
|
+
if oldProvider ~= nil then
|
|
41
|
+
oldProvider.stopScheduler()
|
|
42
|
+
end
|
|
43
|
+
currentProvider = newProvider
|
|
44
|
+
if newProvider ~= nil then
|
|
45
|
+
newProvider.startScheduler()
|
|
46
|
+
end
|
|
47
|
+
return oldProvider
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
--[[
|
|
51
|
+
Returns true if a highly time-critical passage of code is running.
|
|
52
|
+
]]
|
|
53
|
+
function External.isTimeCritical(): boolean
|
|
54
|
+
return timeCritical
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
--[[
|
|
58
|
+
Sends an immediate task to the external provider. Throws if none is set.
|
|
59
|
+
]]
|
|
60
|
+
function External.doTaskImmediate(
|
|
61
|
+
resume: () -> ()
|
|
62
|
+
)
|
|
63
|
+
if currentProvider == nil then
|
|
64
|
+
External.logError("noTaskScheduler")
|
|
65
|
+
else
|
|
66
|
+
currentProvider.doTaskImmediate(resume)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
--[[
|
|
71
|
+
Sends a deferred task to the external provider. Throws if none is set.
|
|
72
|
+
]]
|
|
73
|
+
function External.doTaskDeferred(
|
|
74
|
+
resume: () -> ()
|
|
75
|
+
)
|
|
76
|
+
if currentProvider == nil then
|
|
77
|
+
External.logError("noTaskScheduler")
|
|
78
|
+
else
|
|
79
|
+
currentProvider.doTaskDeferred(resume)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
--[[
|
|
84
|
+
Errors in the current thread and halts execution.
|
|
85
|
+
]]
|
|
86
|
+
function External.logError(
|
|
87
|
+
messageID: string,
|
|
88
|
+
errObj: Types.Error?,
|
|
89
|
+
...: unknown
|
|
90
|
+
): never
|
|
91
|
+
error(formatError(currentProvider, messageID, errObj, ...), 0)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
--[[
|
|
95
|
+
Errors in a different thread to preserve the flow of execution.
|
|
96
|
+
]]
|
|
97
|
+
function External.logErrorNonFatal(
|
|
98
|
+
messageID: string,
|
|
99
|
+
errObj: Types.Error?,
|
|
100
|
+
...: unknown
|
|
101
|
+
): ()
|
|
102
|
+
local errorString = formatError(currentProvider, messageID, errObj, ...)
|
|
103
|
+
if currentProvider ~= nil then
|
|
104
|
+
currentProvider.logErrorNonFatal(errorString)
|
|
105
|
+
else
|
|
106
|
+
print(errorString)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
--[[
|
|
111
|
+
Shows a warning message in the output.
|
|
112
|
+
]]
|
|
113
|
+
function External.logWarn(
|
|
114
|
+
messageID: string,
|
|
115
|
+
...: unknown
|
|
116
|
+
): ()
|
|
117
|
+
local errorString = formatError(currentProvider, messageID, debug.traceback(nil, 2), ...)
|
|
118
|
+
if currentProvider ~= nil then
|
|
119
|
+
currentProvider.logWarn(errorString)
|
|
120
|
+
else
|
|
121
|
+
print(errorString)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
--[[
|
|
126
|
+
Registers a callback to the update step of the external provider.
|
|
127
|
+
Returns a function that can be used to disconnect later.
|
|
128
|
+
|
|
129
|
+
Callbacks are given the current number of seconds since an arbitrary epoch.
|
|
130
|
+
|
|
131
|
+
TODO: This epoch may change between providers. We could investigate ways
|
|
132
|
+
of allowing providers to co-operate to keep the epoch the same, so that
|
|
133
|
+
monotonicity can be better preserved.
|
|
134
|
+
]]
|
|
135
|
+
function External.bindToUpdateStep(
|
|
136
|
+
callback: (
|
|
137
|
+
now: number
|
|
138
|
+
) -> ()
|
|
139
|
+
): () -> ()
|
|
140
|
+
local uniqueIdentifier = {}
|
|
141
|
+
updateStepCallbacks[uniqueIdentifier] = callback
|
|
142
|
+
return function()
|
|
143
|
+
updateStepCallbacks[uniqueIdentifier] = nil
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
--[[
|
|
148
|
+
Steps time-dependent systems with the current number of seconds since an
|
|
149
|
+
arbitrary epoch. This should be called as early as possible in the external
|
|
150
|
+
provider's update cycle.
|
|
151
|
+
]]
|
|
152
|
+
function External.performUpdateStep(
|
|
153
|
+
now: number
|
|
154
|
+
)
|
|
155
|
+
lastUpdateStep = now
|
|
156
|
+
for _, callback in updateStepCallbacks do
|
|
157
|
+
callback(now)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
--[[
|
|
162
|
+
Returns the timestamp of the last update step.
|
|
163
|
+
]]
|
|
164
|
+
function External.lastUpdateStep()
|
|
165
|
+
return lastUpdateStep
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
return External
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Abstraction layer between Fusion internals and external debuggers, allowing
|
|
8
|
+
for deep introspection using function hooks.
|
|
9
|
+
|
|
10
|
+
Unlike `External`, attaching a debugger is optional, and all debugger
|
|
11
|
+
functions are expected to be infallible and non-blocking.
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
local Package = script.Parent
|
|
15
|
+
local Types = require(Package.Types)
|
|
16
|
+
|
|
17
|
+
local currentProvider: Types.ExternalDebugger? = nil
|
|
18
|
+
local lastUpdateStep = 0
|
|
19
|
+
|
|
20
|
+
local Debugger = {}
|
|
21
|
+
|
|
22
|
+
--[[
|
|
23
|
+
Swaps to a new debugger.
|
|
24
|
+
Returns the old debugger, so it can be used again later.
|
|
25
|
+
]]
|
|
26
|
+
function Debugger.setDebugger(
|
|
27
|
+
newProvider: Types.ExternalDebugger?
|
|
28
|
+
): Types.ExternalDebugger?
|
|
29
|
+
local oldProvider = currentProvider
|
|
30
|
+
if oldProvider ~= nil then
|
|
31
|
+
oldProvider.stopDebugging()
|
|
32
|
+
end
|
|
33
|
+
currentProvider = newProvider
|
|
34
|
+
if newProvider ~= nil then
|
|
35
|
+
newProvider.startDebugging()
|
|
36
|
+
end
|
|
37
|
+
return oldProvider
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
--[[
|
|
41
|
+
Called at the earliest moment after a scope is created or removed from the
|
|
42
|
+
scope pool, but not before the scope has finished being prepared by the
|
|
43
|
+
library, so that debuggers can register its existence and track changes
|
|
44
|
+
to the scope over time.
|
|
45
|
+
]]
|
|
46
|
+
function Debugger.trackScope(
|
|
47
|
+
scope: Types.Scope<unknown>
|
|
48
|
+
): ()
|
|
49
|
+
if currentProvider == nil then
|
|
50
|
+
return
|
|
51
|
+
end
|
|
52
|
+
currentProvider.trackScope(scope)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
--[[
|
|
56
|
+
Called at the final moment before a scope is poisoned or added to the scope
|
|
57
|
+
pool, after all cleanup tasks have completed, so that debuggers can erase
|
|
58
|
+
the scope from internal trackers. Note that, due to scope pooling and user
|
|
59
|
+
code, never assume that this correlates with garbage collection events.
|
|
60
|
+
]]
|
|
61
|
+
function Debugger.untrackScope(
|
|
62
|
+
scope: Types.Scope<unknown>
|
|
63
|
+
): ()
|
|
64
|
+
if currentProvider == nil then
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
currentProvider.trackScope(scope)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return Debugger
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
A graph object that runs user code when it's updated by the reactive graph.
|
|
8
|
+
|
|
9
|
+
http://elttob.uk/Fusion/0.3/api-reference/state/types/observer/
|
|
10
|
+
]]
|
|
11
|
+
|
|
12
|
+
local Package = script.Parent.Parent
|
|
13
|
+
local Types = require(Package.Types)
|
|
14
|
+
local External = require(Package.External)
|
|
15
|
+
-- Memory
|
|
16
|
+
local checkLifetime = require(Package.Memory.checkLifetime)
|
|
17
|
+
-- Graph
|
|
18
|
+
local castToGraph = require(Package.Graph.castToGraph)
|
|
19
|
+
local depend = require(Package.Graph.depend)
|
|
20
|
+
local evaluate = require(Package.Graph.evaluate)
|
|
21
|
+
-- Utility
|
|
22
|
+
local nicknames = require(Package.Utility.nicknames)
|
|
23
|
+
|
|
24
|
+
type Self = Types.Observer & {
|
|
25
|
+
_watchingGraph: Types.GraphObject?,
|
|
26
|
+
_changeListeners: {[unknown]: () -> ()}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
local class = {}
|
|
30
|
+
class.type = "Observer"
|
|
31
|
+
class.timeliness = "eager"
|
|
32
|
+
class.dependentSet = table.freeze {}
|
|
33
|
+
|
|
34
|
+
local METATABLE = table.freeze {__index = class}
|
|
35
|
+
|
|
36
|
+
local function Observer(
|
|
37
|
+
scope: Types.Scope<unknown>,
|
|
38
|
+
watching: unknown
|
|
39
|
+
): Types.Observer
|
|
40
|
+
local createdAt = os.clock()
|
|
41
|
+
if watching == nil then
|
|
42
|
+
External.logError("scopeMissing", nil, "Observers", "myScope:Observer(watching)")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
local self: Self = setmetatable(
|
|
46
|
+
{
|
|
47
|
+
scope = scope,
|
|
48
|
+
createdAt = createdAt,
|
|
49
|
+
dependencySet = {},
|
|
50
|
+
lastChange = nil,
|
|
51
|
+
validity = "invalid",
|
|
52
|
+
_watchingGraph = castToGraph(watching),
|
|
53
|
+
_changeListeners = {}
|
|
54
|
+
},
|
|
55
|
+
METATABLE
|
|
56
|
+
) :: any
|
|
57
|
+
local destroy = function()
|
|
58
|
+
self.scope = nil
|
|
59
|
+
for dependency in pairs(self.dependencySet) do
|
|
60
|
+
dependency.dependentSet[self] = nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
self.oldestTask = destroy
|
|
64
|
+
nicknames[self.oldestTask] = "Observer"
|
|
65
|
+
table.insert(scope, destroy)
|
|
66
|
+
|
|
67
|
+
if self._watchingGraph ~= nil then
|
|
68
|
+
checkLifetime.bOutlivesA(
|
|
69
|
+
scope, self.oldestTask,
|
|
70
|
+
self._watchingGraph.scope, self._watchingGraph.oldestTask,
|
|
71
|
+
checkLifetime.formatters.observer
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
-- Eagerly evaluated objects need to evaluate themselves so that they're
|
|
76
|
+
-- valid at all times.
|
|
77
|
+
evaluate(self, true)
|
|
78
|
+
|
|
79
|
+
return self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
function class.onBind(
|
|
83
|
+
self: Self,
|
|
84
|
+
callback: () -> ()
|
|
85
|
+
): () -> ()
|
|
86
|
+
External.doTaskImmediate(callback)
|
|
87
|
+
return self:onChange(callback)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
function class.onChange(
|
|
91
|
+
self: Self,
|
|
92
|
+
callback: () -> ()
|
|
93
|
+
): () -> ()
|
|
94
|
+
local uniqueIdentifier = table.freeze {}
|
|
95
|
+
self._changeListeners[uniqueIdentifier] = callback
|
|
96
|
+
return function()
|
|
97
|
+
self._changeListeners[uniqueIdentifier] = nil
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
function class._evaluate(
|
|
102
|
+
self: Self
|
|
103
|
+
): ()
|
|
104
|
+
if self._watchingGraph ~= nil then
|
|
105
|
+
depend(self, self._watchingGraph)
|
|
106
|
+
end
|
|
107
|
+
for _, callback in self._changeListeners do
|
|
108
|
+
External.doTaskImmediate(callback)
|
|
109
|
+
end
|
|
110
|
+
return true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
table.freeze(class)
|
|
114
|
+
return Observer :: Types.ObserverConstructor
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Returns the input *only* if it is a graph object.
|
|
8
|
+
]]
|
|
9
|
+
|
|
10
|
+
local Package = script.Parent.Parent
|
|
11
|
+
local Types = require(Package.Types)
|
|
12
|
+
|
|
13
|
+
local function castToGraph(
|
|
14
|
+
target: any
|
|
15
|
+
): Types.GraphObject?
|
|
16
|
+
if
|
|
17
|
+
typeof(target) == "table" and
|
|
18
|
+
typeof(target.validity) == "string" and
|
|
19
|
+
typeof(target.timeliness) == "string" and
|
|
20
|
+
typeof(target.dependencySet) == "table" and
|
|
21
|
+
typeof(target.dependentSet) == "table"
|
|
22
|
+
then
|
|
23
|
+
return target
|
|
24
|
+
else
|
|
25
|
+
return nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
return castToGraph
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Prompts a graph object to re-evaluate its own value. If it meaningfully
|
|
8
|
+
changes, then dependents will have to re-evaluate their own values in the
|
|
9
|
+
future.
|
|
10
|
+
|
|
11
|
+
https://fluff.blog/2024/04/16/monotonic-painting.html
|
|
12
|
+
]]
|
|
13
|
+
|
|
14
|
+
local Package = script.Parent.Parent
|
|
15
|
+
local Types = require(Package.Types)
|
|
16
|
+
local External = require(Package.External)
|
|
17
|
+
local evaluate = require(Package.Graph.evaluate)
|
|
18
|
+
|
|
19
|
+
-- How long should this function run before it's considered to be in an infinite
|
|
20
|
+
-- cycle and error out?
|
|
21
|
+
local TERMINATION_TIME = 1
|
|
22
|
+
|
|
23
|
+
local function change(
|
|
24
|
+
target: Types.GraphObject
|
|
25
|
+
): ()
|
|
26
|
+
if target.validity == "busy" then
|
|
27
|
+
return External.logError("infiniteLoop")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
local meaningfullyChanged = evaluate(target, true)
|
|
31
|
+
if not meaningfullyChanged then
|
|
32
|
+
return
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
local searchInNow: {Types.GraphObject} = {}
|
|
36
|
+
local searchInNext: {Types.GraphObject} = {}
|
|
37
|
+
local invalidateList: {Types.GraphObject} = {}
|
|
38
|
+
|
|
39
|
+
searchInNow[1] = target
|
|
40
|
+
local terminateBy = os.clock() + TERMINATION_TIME * External.safetyTimerMultiplier
|
|
41
|
+
repeat
|
|
42
|
+
if os.clock() > terminateBy then
|
|
43
|
+
return External.logError("infiniteLoop")
|
|
44
|
+
end
|
|
45
|
+
local done = true
|
|
46
|
+
for _, searchTarget in searchInNow do
|
|
47
|
+
for dependent in searchTarget.dependentSet do
|
|
48
|
+
if dependent.validity == "valid" then
|
|
49
|
+
done = false
|
|
50
|
+
table.insert(invalidateList, dependent)
|
|
51
|
+
table.insert(searchInNext, dependent)
|
|
52
|
+
elseif dependent.validity == "busy" then
|
|
53
|
+
return External.logError("infiniteLoop")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
searchInNow, searchInNext = searchInNext, searchInNow
|
|
58
|
+
table.clear(searchInNext)
|
|
59
|
+
until done
|
|
60
|
+
|
|
61
|
+
local eagerList: {Types.GraphObject} = {}
|
|
62
|
+
|
|
63
|
+
for _, invalidateTarget in invalidateList do
|
|
64
|
+
invalidateTarget.validity = "invalid"
|
|
65
|
+
if invalidateTarget.timeliness == "eager" then
|
|
66
|
+
table.insert(eagerList, invalidateTarget)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
-- If objects are not executed in order of creations, then dynamic graphs
|
|
70
|
+
-- may experience 'glitches' where nested graph objects see intermediate
|
|
71
|
+
-- values before being destroyed.
|
|
72
|
+
-- https://fluff.blog/2024/07/14/glitches-in-dynamic-reactive-graphs.html
|
|
73
|
+
table.sort(eagerList, function(a, b)
|
|
74
|
+
return a.createdAt < b.createdAt
|
|
75
|
+
end)
|
|
76
|
+
for _, eagerTarget in eagerList do
|
|
77
|
+
evaluate(eagerTarget, false)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
return change
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Forms a dependency on a graph object.
|
|
8
|
+
]]
|
|
9
|
+
|
|
10
|
+
local Package = script.Parent.Parent
|
|
11
|
+
local Types = require(Package.Types)
|
|
12
|
+
local External = require(Package.External)
|
|
13
|
+
local evaluate = require(Package.Graph.evaluate)
|
|
14
|
+
local nameOf = require(Package.Utility.nameOf)
|
|
15
|
+
|
|
16
|
+
local function depend<T>(
|
|
17
|
+
dependent: Types.GraphObject,
|
|
18
|
+
dependency: Types.GraphObject
|
|
19
|
+
): ()
|
|
20
|
+
-- Ensure dependencies are evaluated and up-to-date
|
|
21
|
+
-- when they are depended on. Also, newly created objects
|
|
22
|
+
-- might not have any transitive dependencies captured yet,
|
|
23
|
+
-- so ensure that they're present.
|
|
24
|
+
evaluate(dependency, false)
|
|
25
|
+
|
|
26
|
+
if table.isfrozen(dependent.dependencySet) or table.isfrozen(dependency.dependentSet) then
|
|
27
|
+
External.logError("cannotDepend", nil, nameOf(dependent, "Dependent"), nameOf(dependency, "dependency"))
|
|
28
|
+
end
|
|
29
|
+
dependency.dependentSet[dependent] = true
|
|
30
|
+
dependent.dependencySet[dependency] = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
return depend
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!nolint LocalUnused
|
|
3
|
+
--!nolint LocalShadow
|
|
4
|
+
local task = nil -- Disable usage of Roblox's task scheduler
|
|
5
|
+
|
|
6
|
+
--[[
|
|
7
|
+
Evaluates the graph object if necessary, so that it is up to date.
|
|
8
|
+
Returns true if it meaningfully changed.
|
|
9
|
+
|
|
10
|
+
https://fluff.blog/2024/04/16/monotonic-painting.html
|
|
11
|
+
]]
|
|
12
|
+
|
|
13
|
+
local Package = script.Parent.Parent
|
|
14
|
+
local Types = require(Package.Types)
|
|
15
|
+
local External = require(Package.External)
|
|
16
|
+
|
|
17
|
+
local function evaluate(
|
|
18
|
+
target: Types.GraphObject,
|
|
19
|
+
forceComputation: boolean
|
|
20
|
+
): boolean
|
|
21
|
+
if target.validity == "busy" then
|
|
22
|
+
return External.logError("infiniteLoop")
|
|
23
|
+
end
|
|
24
|
+
local firstEvaluation = target.lastChange == nil
|
|
25
|
+
local isInvalid = target.validity == "invalid"
|
|
26
|
+
if firstEvaluation or isInvalid or forceComputation then
|
|
27
|
+
local needsComputation = firstEvaluation or forceComputation
|
|
28
|
+
if not needsComputation then
|
|
29
|
+
for dependency in target.dependencySet do
|
|
30
|
+
evaluate(dependency, false)
|
|
31
|
+
if dependency.lastChange > target.lastChange then
|
|
32
|
+
needsComputation = true
|
|
33
|
+
break
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
local targetMeaningfullyChanged = false
|
|
38
|
+
if needsComputation then
|
|
39
|
+
for dependency in target.dependencySet do
|
|
40
|
+
dependency.dependentSet[target] = nil
|
|
41
|
+
target.dependencySet[dependency] = nil
|
|
42
|
+
end
|
|
43
|
+
target.validity = "busy"
|
|
44
|
+
targetMeaningfullyChanged = target:_evaluate() or firstEvaluation
|
|
45
|
+
end
|
|
46
|
+
if targetMeaningfullyChanged then
|
|
47
|
+
target.lastChange = os.clock()
|
|
48
|
+
end
|
|
49
|
+
target.validity = "valid"
|
|
50
|
+
return targetMeaningfullyChanged
|
|
51
|
+
else
|
|
52
|
+
return false
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
return evaluate
|